Papaflessas commited on
Commit
4856b42
·
1 Parent(s): ff310f2

Deploy Signal Generator app

Browse files
src/main.py CHANGED
@@ -356,6 +356,16 @@ async def root(request: Request):
356
  """
357
  return templates.TemplateResponse("index.html", {"request": request})
358
 
 
 
 
 
 
 
 
 
 
 
359
  if __name__ == "__main__":
360
  import uvicorn
361
  uvicorn.run(app, host="0.0.0.0", port=7860)
 
356
  """
357
  return templates.TemplateResponse("index.html", {"request": request})
358
 
359
+ @app.get("/logs", response_class=HTMLResponse)
360
+ async def view_logs(request: Request):
361
+ """Serve the Logs Page"""
362
+ return templates.TemplateResponse("logs.html", {"request": request})
363
+
364
+ @app.get("/api/logs")
365
+ async def get_logs():
366
+ """Get recent logs"""
367
+ return {"logs": list(log_buffer)}
368
+
369
  if __name__ == "__main__":
370
  import uvicorn
371
  uvicorn.run(app, host="0.0.0.0", port=7860)
src/orchestrator/coordinator.py CHANGED
@@ -1,18 +1,15 @@
1
- """
2
- Stock Alchemist Coordinator
3
- Orchestrates the following tasks:
4
- 1. Continuous: News Scraper (runs constantly in a separate thread)
5
- 2. Daily at 08:00: Calendar Scraper (fetches today's events)
6
- 3. Weekly (Saturday): Fundamental Analysis (runs for all tickers)
7
- """
8
-
9
  import time
10
  import threading
11
  import sys
12
  import schedule
 
13
  from datetime import datetime, date
14
  from pathlib import Path
15
 
 
 
 
 
16
  # Add src and root to path to ensure imports work correctly
17
  SRC_PATH = Path(__file__).parent.parent
18
  ROOT_PATH = SRC_PATH.parent
@@ -26,8 +23,8 @@ try:
26
  # Import run_saturday_analysis dynamically or add root to path
27
  import run_saturday_analysis
28
  except ImportError as e:
29
- print(f"❌ Import Error: {e}")
30
- print("Ensure you are running from the project root or src directory.")
31
  sys.exit(1)
32
 
33
  class Coordinator:
@@ -38,50 +35,46 @@ class Coordinator:
38
 
39
  def start_news_scraper(self):
40
  """Runs the news scraper in a separate daemon thread"""
41
- print("📰 Starting News Scraper...")
42
  try:
43
  # Run news scraper in a separate thread since it runs constantly
44
  self.news_thread = threading.Thread(target=run_news_scraper_main, daemon=True)
45
  self.news_thread.start()
46
- print("✅ News Scraper started")
47
  except Exception as e:
48
- print(f"❌ Error starting News Scraper: {e}")
49
 
50
  def run_calendar_scraper(self):
51
  """Runs the calendar scraper for today"""
52
- print(f"\n📅 Running Calendar Scraper for {date.today()}...")
53
  try:
54
  today = date.today()
55
  events = get_events(today)
56
  save_events_to_db(events, today)
57
- print("✅ Calendar Scraper finished")
58
  except Exception as e:
59
- print(f"❌ Error running Calendar Scraper: {e}")
60
 
61
  def run_saturday_analysis_task(self):
62
  """Runs the Saturday analysis"""
63
- print("\n📊 Running Saturday Fundamental Analysis...")
64
  try:
65
  run_saturday_analysis.run_saturday_analysis()
66
- print("✅ Saturday Analysis finished")
67
  except Exception as e:
68
- print(f"❌ Error running Saturday Analysis: {e}")
69
 
70
  def scheduler_loop(self):
71
  """Main scheduler loop"""
72
- print("⏰ Scheduler started")
73
 
74
  # Schedule Daily Calendar Scraper at 08:00
75
  schedule.every().day.at("08:00").do(self.run_calendar_scraper)
76
- print(" - Scheduled Calendar Scraper daily at 08:00")
77
 
78
  # Schedule Saturday Analysis every Saturday
79
- # Note: 'at' time is optional, defaulting to running when the script starts if it's Saturday?
80
- # No, schedule.every().saturday.do(...) runs it every saturday.
81
- # Let's pick a time, say 10:00 AM, or just let it run.
82
- # The user said "every saturday run", let's assume a reasonable time like 09:00 AM
83
  schedule.every().saturday.at("09:00").do(self.run_saturday_analysis_task)
84
- print(" - Scheduled Saturday Analysis every Saturday at 09:00")
85
 
86
  while self.is_running:
87
  schedule.run_pending()
@@ -90,14 +83,12 @@ class Coordinator:
90
  def start(self):
91
  """Start the coordinator"""
92
  self.is_running = True
93
- print("🚀 Stock Alchemist Coordinator Starting...")
94
 
95
  # 1. Start News Scraper (Continuous)
96
  self.start_news_scraper()
97
 
98
  # 2. Start Scheduler (Blocking or Threaded?)
99
- # Since news scraper is in a thread, we can run the scheduler in the main thread
100
- # OR run scheduler in a thread and keep main thread for control.
101
  # Let's run scheduler in main thread for simplicity as it just loops.
102
 
103
  try:
@@ -107,9 +98,9 @@ class Coordinator:
107
 
108
  def stop(self):
109
  """Stop the coordinator"""
110
- print("\n🛑 Stopping Coordinator...")
111
  self.is_running = False
112
- print("✅ Coordinator stopped")
113
 
114
  if __name__ == "__main__":
115
  coordinator = Coordinator()
 
 
 
 
 
 
 
 
 
1
  import time
2
  import threading
3
  import sys
4
  import schedule
5
+ import logging
6
  from datetime import datetime, date
7
  from pathlib import Path
8
 
9
+ # Setup logging
10
+ logger = logging.getLogger("coordinator")
11
+ logger.setLevel(logging.INFO)
12
+
13
  # Add src and root to path to ensure imports work correctly
14
  SRC_PATH = Path(__file__).parent.parent
15
  ROOT_PATH = SRC_PATH.parent
 
23
  # Import run_saturday_analysis dynamically or add root to path
24
  import run_saturday_analysis
25
  except ImportError as e:
26
+ logger.error(f"❌ Import Error: {e}")
27
+ logger.error("Ensure you are running from the project root or src directory.")
28
  sys.exit(1)
29
 
30
  class Coordinator:
 
35
 
36
  def start_news_scraper(self):
37
  """Runs the news scraper in a separate daemon thread"""
38
+ logger.info("📰 Starting News Scraper...")
39
  try:
40
  # Run news scraper in a separate thread since it runs constantly
41
  self.news_thread = threading.Thread(target=run_news_scraper_main, daemon=True)
42
  self.news_thread.start()
43
+ logger.info("✅ News Scraper started")
44
  except Exception as e:
45
+ logger.error(f"❌ Error starting News Scraper: {e}")
46
 
47
  def run_calendar_scraper(self):
48
  """Runs the calendar scraper for today"""
49
+ logger.info(f"\n📅 Running Calendar Scraper for {date.today()}...")
50
  try:
51
  today = date.today()
52
  events = get_events(today)
53
  save_events_to_db(events, today)
54
+ logger.info("✅ Calendar Scraper finished")
55
  except Exception as e:
56
+ logger.error(f"❌ Error running Calendar Scraper: {e}")
57
 
58
  def run_saturday_analysis_task(self):
59
  """Runs the Saturday analysis"""
60
+ logger.info("\n📊 Running Saturday Fundamental Analysis...")
61
  try:
62
  run_saturday_analysis.run_saturday_analysis()
63
+ logger.info("✅ Saturday Analysis finished")
64
  except Exception as e:
65
+ logger.error(f"❌ Error running Saturday Analysis: {e}")
66
 
67
  def scheduler_loop(self):
68
  """Main scheduler loop"""
69
+ logger.info("⏰ Scheduler started")
70
 
71
  # Schedule Daily Calendar Scraper at 08:00
72
  schedule.every().day.at("08:00").do(self.run_calendar_scraper)
73
+ logger.info(" - Scheduled Calendar Scraper daily at 08:00")
74
 
75
  # Schedule Saturday Analysis every Saturday
 
 
 
 
76
  schedule.every().saturday.at("09:00").do(self.run_saturday_analysis_task)
77
+ logger.info(" - Scheduled Saturday Analysis every Saturday at 09:00")
78
 
79
  while self.is_running:
80
  schedule.run_pending()
 
83
  def start(self):
84
  """Start the coordinator"""
85
  self.is_running = True
86
+ logger.info("🚀 Stock Alchemist Coordinator Starting...")
87
 
88
  # 1. Start News Scraper (Continuous)
89
  self.start_news_scraper()
90
 
91
  # 2. Start Scheduler (Blocking or Threaded?)
 
 
92
  # Let's run scheduler in main thread for simplicity as it just loops.
93
 
94
  try:
 
98
 
99
  def stop(self):
100
  """Stop the coordinator"""
101
+ logger.info("\n🛑 Stopping Coordinator...")
102
  self.is_running = False
103
+ logger.info("✅ Coordinator stopped")
104
 
105
  if __name__ == "__main__":
106
  coordinator = Coordinator()
src/templates/index.html CHANGED
@@ -165,6 +165,12 @@
165
  </button>
166
  </div>
167
 
 
 
 
 
 
 
168
  <!-- System Stats -->
169
  <div class="card">
170
  <h2>📊 System Status</h2>
 
165
  </button>
166
  </div>
167
 
168
+ <button class="btn" onclick="window.location.href='/logs'" style="flex: 1; background: #334155;">
169
+ View Logs
170
+ </button>
171
+ </div>
172
+ </div>
173
+
174
  <!-- System Stats -->
175
  <div class="card">
176
  <h2>📊 System Status</h2>
src/templates/logs.html ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Stock Alchemist | System Logs</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
8
+ <style>
9
+ :root {
10
+ --bg-color: #0F172A;
11
+ --card-bg: #1E293B;
12
+ --text-primary: #F8FAFC;
13
+ --text-secondary: #94A3B8;
14
+ --accent-green: #10B981;
15
+ --accent-blue: #3B82F6;
16
+ }
17
+
18
+ * { margin: 0; padding: 0; box-sizing: border-box; }
19
+
20
+ body {
21
+ font-family: 'Outfit', sans-serif;
22
+ background-color: var(--bg-color);
23
+ color: var(--text-primary);
24
+ height: 100vh;
25
+ display: flex;
26
+ flex-direction: column;
27
+ padding: 2rem;
28
+ }
29
+
30
+ header {
31
+ display: flex;
32
+ justify-content: space-between;
33
+ align-items: center;
34
+ margin-bottom: 1rem;
35
+ padding-bottom: 1rem;
36
+ border-bottom: 1px solid rgba(255,255,255,0.1);
37
+ }
38
+
39
+ h1 { font-weight: 700; font-size: 1.5rem; }
40
+
41
+ .btn {
42
+ background: var(--card-bg);
43
+ color: var(--text-primary);
44
+ border: 1px solid rgba(255,255,255,0.1);
45
+ padding: 0.5rem 1rem;
46
+ border-radius: 6px;
47
+ cursor: pointer;
48
+ text-decoration: none;
49
+ font-size: 0.9rem;
50
+ transition: all 0.2s;
51
+ }
52
+ .btn:hover { background: #334155; }
53
+
54
+ #log-container {
55
+ flex: 1;
56
+ background: #000;
57
+ color: #d4d4d4;
58
+ font-family: 'Roboto Mono', monospace;
59
+ font-size: 0.85rem;
60
+ padding: 1rem;
61
+ border-radius: 8px;
62
+ overflow-y: auto;
63
+ border: 1px solid rgba(255,255,255,0.1);
64
+ white-space: pre-wrap;
65
+ box-shadow: inset 0 0 20px rgba(0,0,0,0.5);
66
+ }
67
+
68
+ .log-entry { margin-bottom: 2px; }
69
+ .log-info { color: #d4d4d4; }
70
+ .log-error { color: #f87171; }
71
+ .log-warning { color: #fbbf24; }
72
+
73
+ .controls {
74
+ display: flex;
75
+ gap: 1rem;
76
+ margin-bottom: 1rem;
77
+ align-items: center;
78
+ }
79
+
80
+ #status-dot {
81
+ width: 8px; height: 8px;
82
+ border-radius: 50%;
83
+ background: var(--accent-green);
84
+ display: inline-block;
85
+ margin-right: 5px;
86
+ }
87
+ </style>
88
+ </head>
89
+ <body>
90
+
91
+ <header>
92
+ <div style="display:flex; align-items:center; gap:1rem;">
93
+ <h1>📜 System Logs</h1>
94
+ <span style="font-size: 0.9rem; color: var(--text-secondary);"><span id="status-dot"></span>Live</span>
95
+ </div>
96
+ <a href="/" class="btn">← Back to Dashboard</a>
97
+ </header>
98
+
99
+ <div class="controls">
100
+ <button class="btn" onclick="fetchLogs()">Refresh Now</button>
101
+ <label style="font-size: 0.9rem; display:flex; align-items:center; gap:0.5rem; cursor:pointer;">
102
+ <input type="checkbox" id="autoScroll" checked> Auto-scroll
103
+ </label>
104
+ </div>
105
+
106
+ <div id="log-container">Loading logs...</div>
107
+
108
+ <script>
109
+ const container = document.getElementById('log-container');
110
+ let autoScroll = true;
111
+
112
+ document.getElementById('autoScroll').addEventListener('change', (e) => {
113
+ autoScroll = e.target.checked;
114
+ });
115
+
116
+ async function fetchLogs() {
117
+ try {
118
+ const res = await fetch('/api/logs');
119
+ const data = await res.json();
120
+
121
+ if (data.logs && data.logs.length > 0) {
122
+ container.innerHTML = data.logs.map(line => {
123
+ let cls = 'log-info';
124
+ if (line.includes('ERROR') || line.includes('❌')) cls = 'log-error';
125
+ else if (line.includes('WARNING') || line.includes('⚠️')) cls = 'log-warning';
126
+ return `<div class="log-entry ${cls}">${line}</div>`;
127
+ }).join('');
128
+
129
+ if (autoScroll) {
130
+ container.scrollTop = container.scrollHeight;
131
+ }
132
+ }
133
+ } catch (e) {
134
+ console.error("Failed to fetch logs", e);
135
+ }
136
+ }
137
+
138
+ // Poll every 3 seconds
139
+ fetchLogs();
140
+ setInterval(fetchLogs, 3000);
141
+ </script>
142
+ </body>
143
+ </html>