mgbam commited on
Commit
2f96557
Β·
verified Β·
1 Parent(s): 01e217d

Update app/app.py

Browse files
Files changed (1) hide show
  1. app/app.py +33 -26
app/app.py CHANGED
@@ -18,14 +18,10 @@ from fastapi import FastAPI, Request
18
  from fastapi.responses import HTMLResponse, StreamingResponse
19
  from fastapi.templating import Jinja2Templates
20
 
21
- # ====================================================================
22
- # FIX APPLIED HERE
23
- # ====================================================================
24
  # Use relative imports because these modules are in the same 'app' package.
25
  from .price_fetcher import PriceFetcher
26
  from .gemini_analyzer import GeminiAnalyzer
27
  from newsapi import NewsApiClient
28
- # ====================================================================
29
 
30
 
31
  # --- Application Lifespan for Resource Management ---
@@ -37,20 +33,17 @@ async def lifespan(app: FastAPI):
37
  lifespan context manager.
38
  """
39
  async with httpx.AsyncClient() as client:
40
- # Instantiate and store all services in the application state.
41
  app.state.price_fetcher = PriceFetcher(client=client, coins=["bitcoin", "ethereum", "dogecoin"])
42
  app.state.gemini_analyzer = GeminiAnalyzer(client=client)
43
  app.state.news_api = NewsApiClient(api_key=os.getenv("NEWS_API_KEY"))
44
-
45
- # Create a queue for the real-time signal feed
46
  app.state.signal_queue: asyncio.Queue = asyncio.Queue()
47
 
48
- # Create cancellable background tasks for periodic updates.
49
  price_task = asyncio.create_task(
50
  run_periodic_updates(app.state.price_fetcher, interval_seconds=60)
51
  )
52
  news_task = asyncio.create_task(
53
- run_periodic_news_analysis(app, interval_seconds=900) # Run every 15 minutes
54
  )
55
 
56
  print("πŸš€ Sentinel TradeFlow Protocol started successfully.")
@@ -72,42 +65,57 @@ async def run_periodic_updates(fetcher: PriceFetcher, interval_seconds: int):
72
  await asyncio.sleep(interval_seconds)
73
 
74
  async def run_periodic_news_analysis(app: FastAPI, interval_seconds: int):
75
- """Fetches, analyzes, and queues top crypto news periodically."""
76
  while True:
77
- print("πŸ“° Fetching latest crypto news for automated analysis...")
78
  try:
79
  top_headlines = app.state.news_api.get_everything(
80
- q='bitcoin OR ethereum OR crypto OR blockchain',
81
  language='en',
82
  sort_by='publishedAt',
83
  page_size=5
84
  )
 
 
 
 
 
 
 
 
 
85
  analyzer: GeminiAnalyzer = app.state.gemini_analyzer
86
- for article in top_headlines.get('articles', []):
87
  title = article.get('title')
88
- if title and "[Removed]" not in title:
89
- analysis = await analyzer.analyze_text(title)
90
- if analysis.get("error"):
91
- print(f"Skipping article due to Gemini error: {analysis['reason']}")
92
- continue
93
-
94
- analysis['url'] = article.get('url')
95
- await app.state.signal_queue.put(analysis)
96
- print(f"βœ… Signal generated and queued for: {title}")
 
 
 
 
 
 
 
97
 
98
  except Exception as e:
99
- print(f"❌ Error during news fetching or analysis: {e}")
100
 
 
101
  await asyncio.sleep(interval_seconds)
102
 
103
  # --- FastAPI App Initialization ---
104
 
105
  app = FastAPI(title="Sentinel TradeFlow Protocol", lifespan=lifespan)
106
- # This path is relative to the root where the uvicorn command is run
107
  templates = Jinja2Templates(directory="templates")
108
 
109
  # --- HTML Rendering Helper ---
110
-
111
  def render_signal_card(payload: dict) -> str:
112
  """Renders a dictionary of analysis into a styled HTML card."""
113
  s = payload
@@ -133,7 +141,6 @@ def render_signal_card(payload: dict) -> str:
133
  """
134
 
135
  # --- API Endpoints ---
136
-
137
  @app.get("/", response_class=HTMLResponse)
138
  async def serve_dashboard(request: Request):
139
  """Serves the main interactive dashboard from `index.html`."""
 
18
  from fastapi.responses import HTMLResponse, StreamingResponse
19
  from fastapi.templating import Jinja2Templates
20
 
 
 
 
21
  # Use relative imports because these modules are in the same 'app' package.
22
  from .price_fetcher import PriceFetcher
23
  from .gemini_analyzer import GeminiAnalyzer
24
  from newsapi import NewsApiClient
 
25
 
26
 
27
  # --- Application Lifespan for Resource Management ---
 
33
  lifespan context manager.
34
  """
35
  async with httpx.AsyncClient() as client:
 
36
  app.state.price_fetcher = PriceFetcher(client=client, coins=["bitcoin", "ethereum", "dogecoin"])
37
  app.state.gemini_analyzer = GeminiAnalyzer(client=client)
38
  app.state.news_api = NewsApiClient(api_key=os.getenv("NEWS_API_KEY"))
 
 
39
  app.state.signal_queue: asyncio.Queue = asyncio.Queue()
40
 
41
+ # Create cancellable background tasks. Let's use a shorter timer for testing.
42
  price_task = asyncio.create_task(
43
  run_periodic_updates(app.state.price_fetcher, interval_seconds=60)
44
  )
45
  news_task = asyncio.create_task(
46
+ run_periodic_news_analysis(app, interval_seconds=300) # Check news every 5 minutes for debugging
47
  )
48
 
49
  print("πŸš€ Sentinel TradeFlow Protocol started successfully.")
 
65
  await asyncio.sleep(interval_seconds)
66
 
67
  async def run_periodic_news_analysis(app: FastAPI, interval_seconds: int):
68
+ """Fetches, analyzes, and queues top crypto news periodically with detailed logging."""
69
  while True:
70
+ print("πŸ“° [1/5] Fetching latest crypto news...")
71
  try:
72
  top_headlines = app.state.news_api.get_everything(
73
+ q='bitcoin OR ethereum OR "binance coin" OR solana OR ripple OR cardano',
74
  language='en',
75
  sort_by='publishedAt',
76
  page_size=5
77
  )
78
+
79
+ articles = top_headlines.get('articles', [])
80
+ print(f"πŸ“° [2/5] NewsAPI call successful. Found {len(articles)} articles.")
81
+
82
+ if not articles:
83
+ print("πŸ“° [SKIP] No new articles found in this cycle.")
84
+ await asyncio.sleep(interval_seconds)
85
+ continue
86
+
87
  analyzer: GeminiAnalyzer = app.state.gemini_analyzer
88
+ for article in articles:
89
  title = article.get('title')
90
+ print(f"πŸ“° [3/5] Processing article: '{title}'")
91
+
92
+ if not title or "[Removed]" in title:
93
+ print(f"πŸ“° [SKIP] Article has no title or was removed.")
94
+ continue
95
+
96
+ print(f"πŸ“° [4/5] Sending to Gemini for analysis...")
97
+ analysis = await analyzer.analyze_text(title)
98
+
99
+ if analysis.get("error"):
100
+ print(f"❌ [SKIP] Gemini analysis failed for '{title}'. Reason: {analysis.get('reason')}")
101
+ continue
102
+
103
+ analysis['url'] = article.get('url')
104
+ await app.state.signal_queue.put(analysis)
105
+ print(f"βœ… [5/5] Signal generated and queued for: '{title}'")
106
 
107
  except Exception as e:
108
+ print(f"❌❌❌ CRITICAL ERROR in news analysis loop: {e}")
109
 
110
+ print(f"πŸ“° Loop finished. Waiting for {interval_seconds} seconds.")
111
  await asyncio.sleep(interval_seconds)
112
 
113
  # --- FastAPI App Initialization ---
114
 
115
  app = FastAPI(title="Sentinel TradeFlow Protocol", lifespan=lifespan)
 
116
  templates = Jinja2Templates(directory="templates")
117
 
118
  # --- HTML Rendering Helper ---
 
119
  def render_signal_card(payload: dict) -> str:
120
  """Renders a dictionary of analysis into a styled HTML card."""
121
  s = payload
 
141
  """
142
 
143
  # --- API Endpoints ---
 
144
  @app.get("/", response_class=HTMLResponse)
145
  async def serve_dashboard(request: Request):
146
  """Serves the main interactive dashboard from `index.html`."""