shyameati commited on
Commit
da1806a
·
1 Parent(s): 36bd0e2

Update app.py to serve index.html and improve API

Browse files
Files changed (1) hide show
  1. app.py +72 -108
app.py CHANGED
@@ -1,136 +1,100 @@
 
1
  import logging
2
- from fastapi import FastAPI
3
- from datasets import load_dataset
 
4
  import pandas as pd
5
  import numpy as np
 
6
 
7
- # -------------------------------------------------
8
- # Logging configuration
9
- # -------------------------------------------------
 
 
10
  logging.basicConfig(
11
  level=logging.INFO,
12
- format="%(asctime)s | %(levelname)s | %(message)s"
13
  )
14
  logger = logging.getLogger(__name__)
15
 
16
- app = FastAPI()
17
-
18
- # -------------------------------------------------
19
- # Lazy dataset cache
20
- # -------------------------------------------------
21
- DF = None
22
-
23
-
24
- def load_data_once():
25
- """
26
- Load the dataset only once using streaming mode.
27
- Hugging Face Spaces cannot download large datasets at startup,
28
- so streaming=True avoids timeouts and memory issues.
29
- """
30
- global DF
31
-
32
- if DF is None:
33
- logger.info("Loading dataset (streaming mode): kurry/sp500_earnings_transcripts ...")
34
-
35
- ds = load_dataset(
36
- "kurry/sp500_earnings_transcripts",
37
- split="train",
38
- streaming=True
39
- )
40
-
41
- # Convert streaming dataset → pandas DataFrame
42
- # Limit rows to avoid memory overload
43
- rows = []
44
- for i, item in enumerate(ds):
45
- rows.append(item)
46
- if i > 5000: # Safety limit for Spaces
47
- break
48
-
49
- DF = pd.DataFrame(rows)
50
- logger.info(f"Loaded {len(DF)} rows into DataFrame")
51
-
52
- return DF
53
-
54
 
55
- # -------------------------------------------------
56
- # Utility: convert NumPy → Python
57
- # -------------------------------------------------
58
- def to_python(obj):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  if isinstance(obj, (np.integer, np.int64)):
60
  return int(obj)
61
  if isinstance(obj, (np.floating, np.float64)):
62
  return float(obj)
63
- if isinstance(obj, (np.bool_,)):
64
- return bool(obj)
65
- if isinstance(obj, pd.Timestamp):
66
- return obj.isoformat()
67
  return obj
68
 
69
-
70
- def clean_dict(d):
71
- return {k: to_python(v) for k, v in d.items()}
72
-
73
-
74
- # -------------------------------------------------
75
- # Routes
76
- # -------------------------------------------------
77
-
78
- @app.on_event("startup")
79
- def startup_event():
80
- logger.info("🚀 Earnings Transcript API starting up")
81
-
82
-
83
- @app.get("/")
84
- def root():
85
- logger.info("Root endpoint called")
86
- return {"message": "Earnings Transcript API is running"}
87
-
88
-
89
  @app.get("/tickers")
90
- def list_tickers():
91
- logger.info("Listing all tickers")
92
- df = load_data_once()
93
-
94
- if "symbol" not in df.columns:
95
- return {"error": "Dataset does not contain 'symbol' column"}
96
-
97
- tickers = sorted(df["symbol"].dropna().unique().tolist())
98
- logger.info(f"Returned {len(tickers)} tickers")
99
  return {"tickers": tickers}
100
 
101
-
 
 
102
  @app.get("/transcript/{symbol}")
103
  def get_transcript(symbol: str):
104
- logger.info(f"Transcript request received for symbol: {symbol}")
105
 
106
- df = load_data_once()
107
  symbol = symbol.upper()
 
108
 
109
- if "symbol" not in df.columns:
110
- return {"error": "Dataset missing 'symbol' column"}
111
-
112
- rows = df[df["symbol"] == symbol]
113
-
114
- if rows.empty:
115
- logger.warning(f"No transcripts found for symbol: {symbol}")
116
- return {"error": f"No transcripts found for symbol {symbol}"}
117
-
118
- row = rows.iloc[0]
119
- base_info = clean_dict(row.to_dict())
120
 
121
- # Extract structured content (correct column name)
122
- segments = row.get("structured_content", None)
123
 
124
- if isinstance(segments, list):
125
- logger.info(f"Cleaning {len(segments)} segments for {symbol}")
126
- cleaned_segments = [
127
- clean_dict(seg) for seg in segments if isinstance(seg, dict)
128
- ]
129
- base_info["segments"] = cleaned_segments
130
- else:
131
- logger.info(f"No structured_content found for {symbol}")
132
- base_info["segments"] = []
133
 
134
- logger.info(f"Returning transcript for {symbol}")
135
- return base_info
136
 
 
1
+ import json
2
  import logging
3
+ from fastapi import FastAPI, HTTPException
4
+ from fastapi.responses import HTMLResponse
5
+ from fastapi.middleware.cors import CORSMiddleware
6
  import pandas as pd
7
  import numpy as np
8
+ import os
9
 
10
+ app = FastAPI()
11
+
12
+ # ---------------------------------------------------------
13
+ # Logging
14
+ # ---------------------------------------------------------
15
  logging.basicConfig(
16
  level=logging.INFO,
17
+ format="%(asctime)s [%(levelname)s] %(message)s"
18
  )
19
  logger = logging.getLogger(__name__)
20
 
21
+ # ---------------------------------------------------------
22
+ # CORS (optional but useful for frontend)
23
+ # ---------------------------------------------------------
24
+ app.add_middleware(
25
+ CORSMiddleware,
26
+ allow_origins=["*"],
27
+ allow_methods=["*"],
28
+ allow_headers=["*"],
29
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
 
31
+ # ---------------------------------------------------------
32
+ # Lazy-loaded dataset
33
+ # ---------------------------------------------------------
34
+ DATASET_PATH = "transcripts.csv" # change if needed
35
+ df_cache = None
36
+
37
+ def load_dataset():
38
+ global df_cache
39
+ if df_cache is None:
40
+ logger.info("Loading dataset...")
41
+ if not os.path.exists(DATASET_PATH):
42
+ raise FileNotFoundError(f"Dataset not found: {DATASET_PATH}")
43
+ df_cache = pd.read_csv(DATASET_PATH)
44
+ logger.info(f"Dataset loaded with {len(df_cache)} rows.")
45
+ return df_cache
46
+
47
+ # ---------------------------------------------------------
48
+ # JSON-safe conversion
49
+ # ---------------------------------------------------------
50
+ def to_json_safe(obj):
51
  if isinstance(obj, (np.integer, np.int64)):
52
  return int(obj)
53
  if isinstance(obj, (np.floating, np.float64)):
54
  return float(obj)
55
+ if isinstance(obj, (np.ndarray, list)):
56
+ return [to_json_safe(x) for x in obj]
57
+ if isinstance(obj, dict):
58
+ return {k: to_json_safe(v) for k, v in obj.items()}
59
  return obj
60
 
61
+ # ---------------------------------------------------------
62
+ # Serve index.html at "/"
63
+ # ---------------------------------------------------------
64
+ @app.get("/", response_class=HTMLResponse)
65
+ def serve_index():
66
+ if not os.path.exists("index.html"):
67
+ return "<h1>index.html not found</h1>"
68
+ with open("index.html", "r") as f:
69
+ return f.read()
70
+
71
+ # ---------------------------------------------------------
72
+ # List all tickers
73
+ # ---------------------------------------------------------
 
 
 
 
 
 
 
74
  @app.get("/tickers")
75
+ def get_tickers():
76
+ df = load_dataset()
77
+ tickers = sorted(df["symbol"].unique().tolist())
 
 
 
 
 
 
78
  return {"tickers": tickers}
79
 
80
+ # ---------------------------------------------------------
81
+ # Get transcript for a symbol
82
+ # ---------------------------------------------------------
83
  @app.get("/transcript/{symbol}")
84
  def get_transcript(symbol: str):
85
+ df = load_dataset()
86
 
 
87
  symbol = symbol.upper()
88
+ logger.info(f"Fetching transcript for: {symbol}")
89
 
90
+ subset = df[df["symbol"] == symbol]
 
 
 
 
 
 
 
 
 
 
91
 
92
+ if subset.empty:
93
+ raise HTTPException(status_code=404, detail=f"No transcript found for {symbol}")
94
 
95
+ # Convert each row to JSON-safe dict
96
+ records = subset.to_dict(orient="records")
97
+ safe_records = [to_json_safe(r) for r in records]
 
 
 
 
 
 
98
 
99
+ return {"symbol": symbol, "records": safe_records}
 
100