victor HF Staff Claude commited on
Commit
4c75ecc
Β·
1 Parent(s): f568c4f

Add analytics feature to track daily request counts

Browse files

- Add analytics.py module for request tracking with file-based storage
- Integrate request logging into search_web function
- Add bar chart visualization showing last 30 days of requests
- Add filelock dependency for thread-safe file operations

πŸ€– Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

Files changed (3) hide show
  1. analytics.py +38 -0
  2. app.py +21 -2
  3. requirements.txt +2 -1
analytics.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ─── analytics.py ──────────────────────────────────────────────────────────────
2
+ import os
3
+ import json
4
+ from datetime import datetime, timedelta, timezone
5
+ from filelock import FileLock # pip install filelock
6
+ import pandas as pd # already available in HF images
7
+
8
+ COUNTS_FILE = "/data/request_counts.json"
9
+ LOCK_FILE = COUNTS_FILE + ".lock"
10
+
11
+ def _load() -> dict:
12
+ if not os.path.exists(COUNTS_FILE):
13
+ return {}
14
+ with open(COUNTS_FILE) as f:
15
+ return json.load(f)
16
+
17
+ def _save(data: dict):
18
+ with open(COUNTS_FILE, "w") as f:
19
+ json.dump(data, f)
20
+
21
+ async def record_request() -> None:
22
+ """Increment today's counter (UTC) atomically."""
23
+ today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
24
+ with FileLock(LOCK_FILE):
25
+ data = _load()
26
+ data[today] = data.get(today, 0) + 1
27
+ _save(data)
28
+
29
+ def last_n_days_df(n: int = 30) -> pd.DataFrame:
30
+ """Return a DataFrame with a row for each of the past *n* days."""
31
+ now = datetime.now(timezone.utc)
32
+ with FileLock(LOCK_FILE):
33
+ data = _load()
34
+ records = []
35
+ for i in range(n):
36
+ day = (now - timedelta(days=n - 1 - i)).strftime("%Y-%m-%d")
37
+ records.append({"date": day, "count": data.get(day, 0)})
38
+ return pd.DataFrame(records)
app.py CHANGED
@@ -9,6 +9,7 @@ from dateutil import parser as dateparser
9
  from limits import parse
10
  from limits.aio.storage import MemoryStorage
11
  from limits.aio.strategies import MovingWindowRateLimiter
 
12
 
13
  # Configuration
14
  SERPER_API_KEY = os.getenv("SERPER_API_KEY")
@@ -25,6 +26,7 @@ rate_limit = parse("360/hour")
25
  async def search_web(
26
  query: str, search_type: str = "search", num_results: Optional[int] = 4
27
  ) -> str:
 
28
  """
29
  Search the web for information or fresh news, returning extracted content.
30
 
@@ -184,6 +186,12 @@ async def search_web(
184
  return f"Error occurred while searching: {str(e)}. Please try again or check your query."
185
 
186
 
 
 
 
 
 
 
187
  # Create Gradio interface
188
  with gr.Blocks(title="Web Search MCP Server") as demo:
189
  gr.HTML(
@@ -259,6 +267,17 @@ with gr.Blocks(title="Web Search MCP Server") as demo:
259
  info="The extracted article content will appear here",
260
  )
261
 
 
 
 
 
 
 
 
 
 
 
 
262
  # Add examples
263
  gr.Examples(
264
  examples=[
@@ -275,9 +294,9 @@ with gr.Blocks(title="Web Search MCP Server") as demo:
275
  )
276
 
277
  search_button.click(
278
- fn=search_web,
279
  inputs=[query_input, search_type_input, num_results_input],
280
- outputs=output,
281
  )
282
 
283
 
 
9
  from limits import parse
10
  from limits.aio.storage import MemoryStorage
11
  from limits.aio.strategies import MovingWindowRateLimiter
12
+ from analytics import record_request, last_n_days_df
13
 
14
  # Configuration
15
  SERPER_API_KEY = os.getenv("SERPER_API_KEY")
 
26
  async def search_web(
27
  query: str, search_type: str = "search", num_results: Optional[int] = 4
28
  ) -> str:
29
+ await record_request()
30
  """
31
  Search the web for information or fresh news, returning extracted content.
32
 
 
186
  return f"Error occurred while searching: {str(e)}. Please try again or check your query."
187
 
188
 
189
+ async def search_and_log(query, search_type, num_results):
190
+ text = await search_web(query, search_type, num_results)
191
+ chart_df = last_n_days_df() # default = 30 days
192
+ return text, chart_df
193
+
194
+
195
  # Create Gradio interface
196
  with gr.Blocks(title="Web Search MCP Server") as demo:
197
  gr.HTML(
 
267
  info="The extracted article content will appear here",
268
  )
269
 
270
+ # ════════════════════════════ NEW ════════════════════════════
271
+ requests_plot = gr.BarPlot(
272
+ value=last_n_days_df(), # initial empty/old data
273
+ x="date",
274
+ y="count",
275
+ title="Requests per Day (Last 30 days)",
276
+ tooltip="all",
277
+ height=320, # tweak to taste
278
+ )
279
+ # ═══════════════════════════════════════════════════════════════
280
+
281
  # Add examples
282
  gr.Examples(
283
  examples=[
 
294
  )
295
 
296
  search_button.click(
297
+ fn=search_and_log, # wrapper
298
  inputs=[query_input, search_type_input, num_results_input],
299
+ outputs=[output, requests_plot], # update both
300
  )
301
 
302
 
requirements.txt CHANGED
@@ -2,4 +2,5 @@ gradio
2
  httpx
3
  trafilatura
4
  python-dateutil
5
- limits
 
 
2
  httpx
3
  trafilatura
4
  python-dateutil
5
+ limits
6
+ filelock