deepdiver 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- deepdiver/__init__.py +38 -0
- deepdiver/content_processor.py +343 -0
- deepdiver/deepdive.py +801 -0
- deepdiver/deepdiver.yaml +79 -0
- deepdiver/notebooklm_automator.py +1441 -0
- deepdiver/podcast_manager.py +402 -0
- deepdiver/session_tracker.py +723 -0
- deepdiver-0.1.0.dist-info/METADATA +455 -0
- deepdiver-0.1.0.dist-info/RECORD +13 -0
- deepdiver-0.1.0.dist-info/WHEEL +5 -0
- deepdiver-0.1.0.dist-info/entry_points.txt +2 -0
- deepdiver-0.1.0.dist-info/licenses/LICENSE +21 -0
- deepdiver-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Session Tracker Module
|
|
3
|
+
Part of DeepDiver - NotebookLM Podcast Automation System
|
|
4
|
+
|
|
5
|
+
This module handles session management, tracking, and metadata
|
|
6
|
+
for DeepDiver podcast creation sessions.
|
|
7
|
+
|
|
8
|
+
Assembly Team: Jerry ⚡, Nyro ♠️, Aureon 🌿, JamAI 🎸, Synth 🧵
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import os
|
|
14
|
+
import uuid
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Dict, List, Optional, Any
|
|
18
|
+
|
|
19
|
+
import yaml
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class SessionTracker:
|
|
23
|
+
"""
|
|
24
|
+
Manages DeepDiver sessions and metadata.
|
|
25
|
+
|
|
26
|
+
This class handles session creation, tracking, and persistence
|
|
27
|
+
for podcast creation workflows.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
|
31
|
+
"""Initialize the session tracker with configuration."""
|
|
32
|
+
self.config = config or {}
|
|
33
|
+
self.logger = self._setup_logging()
|
|
34
|
+
|
|
35
|
+
# Session settings
|
|
36
|
+
self.session_dir = self.config.get('SESSION_TRACKING', {}).get(
|
|
37
|
+
'session_dir', './sessions'
|
|
38
|
+
)
|
|
39
|
+
self.metadata_format = self.config.get('SESSION_TRACKING', {}).get(
|
|
40
|
+
'metadata_format', 'yaml'
|
|
41
|
+
)
|
|
42
|
+
self.auto_save = self.config.get('SESSION_TRACKING', {}).get(
|
|
43
|
+
'auto_save', True
|
|
44
|
+
)
|
|
45
|
+
self.max_sessions = self.config.get('SESSION_TRACKING', {}).get(
|
|
46
|
+
'max_sessions', 100
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Ensure session directory exists
|
|
50
|
+
os.makedirs(self.session_dir, exist_ok=True)
|
|
51
|
+
|
|
52
|
+
# Current session
|
|
53
|
+
self.current_session = None
|
|
54
|
+
self.session_file = os.path.join(self.session_dir, 'current_session.json')
|
|
55
|
+
|
|
56
|
+
self.logger.info("♠️🌿🎸🧵 SessionTracker initialized")
|
|
57
|
+
|
|
58
|
+
def _setup_logging(self) -> logging.Logger:
|
|
59
|
+
"""Set up logging configuration."""
|
|
60
|
+
logger = logging.getLogger('SessionTracker')
|
|
61
|
+
logger.setLevel(logging.INFO)
|
|
62
|
+
|
|
63
|
+
if not logger.handlers:
|
|
64
|
+
handler = logging.StreamHandler()
|
|
65
|
+
formatter = logging.Formatter(
|
|
66
|
+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
67
|
+
)
|
|
68
|
+
handler.setFormatter(formatter)
|
|
69
|
+
logger.addHandler(handler)
|
|
70
|
+
|
|
71
|
+
return logger
|
|
72
|
+
|
|
73
|
+
def start_session(self, ai_assistant: str = 'claude',
|
|
74
|
+
issue_number: Optional[int] = None,
|
|
75
|
+
agents: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
76
|
+
"""
|
|
77
|
+
Start a new DeepDiver session.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
ai_assistant (str): Name of the AI assistant
|
|
81
|
+
issue_number (int, optional): Associated issue number
|
|
82
|
+
agents (List[str], optional): List of active agents
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
Dict[str, Any]: Session information
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
# Generate session ID
|
|
89
|
+
session_id = str(uuid.uuid4())
|
|
90
|
+
|
|
91
|
+
# Default agents
|
|
92
|
+
if agents is None:
|
|
93
|
+
agents = ['Jerry ⚡', 'Nyro ♠️', 'Aureon 🌿', 'JamAI 🎸', 'Synth 🧵']
|
|
94
|
+
|
|
95
|
+
# Create session data
|
|
96
|
+
session_data = {
|
|
97
|
+
'session_id': session_id,
|
|
98
|
+
'ai_assistant': ai_assistant,
|
|
99
|
+
'agents': agents,
|
|
100
|
+
'issue_number': issue_number,
|
|
101
|
+
'pr_number': None,
|
|
102
|
+
'created_at': datetime.now().isoformat(),
|
|
103
|
+
'status': 'active',
|
|
104
|
+
'podcasts_created': [],
|
|
105
|
+
'documents_processed': [],
|
|
106
|
+
'notebooks': [],
|
|
107
|
+
'active_notebook_id': None,
|
|
108
|
+
'notes': [],
|
|
109
|
+
'assembly_team': {
|
|
110
|
+
'leader': 'Jerry ⚡',
|
|
111
|
+
'nyro': '♠️ Structural Architect',
|
|
112
|
+
'aureon': '🌿 Emotional Context',
|
|
113
|
+
'jamai': '🎸 Musical Harmony',
|
|
114
|
+
'synth': '🧵 Terminal Orchestration'
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# Save session
|
|
119
|
+
self.current_session = session_data
|
|
120
|
+
self._save_current_session()
|
|
121
|
+
|
|
122
|
+
# Create session file
|
|
123
|
+
session_filename = f"session_{session_id}.json"
|
|
124
|
+
session_path = os.path.join(self.session_dir, session_filename)
|
|
125
|
+
|
|
126
|
+
with open(session_path, 'w', encoding='utf-8') as f:
|
|
127
|
+
json.dump(session_data, f, indent=2, ensure_ascii=False)
|
|
128
|
+
|
|
129
|
+
self.logger.info(f"✅ Session started: {session_id}")
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
'success': True,
|
|
133
|
+
'session_id': session_id,
|
|
134
|
+
'session_data': session_data,
|
|
135
|
+
'session_path': session_path
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
except Exception as e:
|
|
139
|
+
self.logger.error(f"❌ Failed to start session: {e}")
|
|
140
|
+
return {
|
|
141
|
+
'success': False,
|
|
142
|
+
'error': str(e)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
def write_to_session(self, message: str, message_type: str = 'note') -> bool:
|
|
146
|
+
"""
|
|
147
|
+
Write a message to the current session.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
message (str): Message to write
|
|
151
|
+
message_type (str): Type of message (note, podcast, document, etc.)
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
bool: True if write successful, False otherwise
|
|
155
|
+
"""
|
|
156
|
+
try:
|
|
157
|
+
if not self.current_session:
|
|
158
|
+
self.logger.warning("No active session to write to")
|
|
159
|
+
return False
|
|
160
|
+
|
|
161
|
+
# Create message entry
|
|
162
|
+
message_entry = {
|
|
163
|
+
'timestamp': datetime.now().isoformat(),
|
|
164
|
+
'type': message_type,
|
|
165
|
+
'message': message
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
# Add to session notes
|
|
169
|
+
self.current_session['notes'].append(message_entry)
|
|
170
|
+
|
|
171
|
+
# Auto-save if enabled
|
|
172
|
+
if self.auto_save:
|
|
173
|
+
self._save_current_session()
|
|
174
|
+
|
|
175
|
+
self.logger.info(f"✅ Message written to session: {message_type}")
|
|
176
|
+
return True
|
|
177
|
+
|
|
178
|
+
except Exception as e:
|
|
179
|
+
self.logger.error(f"❌ Failed to write to session: {e}")
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
def add_podcast_to_session(self, podcast_info: Dict[str, Any]) -> bool:
|
|
183
|
+
"""
|
|
184
|
+
Add a podcast to the current session.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
podcast_info (Dict[str, Any]): Information about the created podcast
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
bool: True if add successful, False otherwise
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
if not self.current_session:
|
|
194
|
+
self.logger.warning("No active session to add podcast to")
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
# Add podcast info
|
|
198
|
+
podcast_entry = {
|
|
199
|
+
'timestamp': datetime.now().isoformat(),
|
|
200
|
+
'podcast_info': podcast_info
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
self.current_session['podcasts_created'].append(podcast_entry)
|
|
204
|
+
|
|
205
|
+
# Auto-save if enabled
|
|
206
|
+
if self.auto_save:
|
|
207
|
+
self._save_current_session()
|
|
208
|
+
|
|
209
|
+
self.logger.info(f"✅ Podcast added to session: {podcast_info.get('title', 'Unknown')}")
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
self.logger.error(f"❌ Failed to add podcast to session: {e}")
|
|
214
|
+
return False
|
|
215
|
+
|
|
216
|
+
def add_document_to_session(self, document_info: Dict[str, Any]) -> bool:
|
|
217
|
+
"""
|
|
218
|
+
Add a document to the current session.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
document_info (Dict[str, Any]): Information about the processed document
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
bool: True if add successful, False otherwise
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
if not self.current_session:
|
|
228
|
+
self.logger.warning("No active session to add document to")
|
|
229
|
+
return False
|
|
230
|
+
|
|
231
|
+
# Add document info
|
|
232
|
+
document_entry = {
|
|
233
|
+
'timestamp': datetime.now().isoformat(),
|
|
234
|
+
'document_info': document_info
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
self.current_session['documents_processed'].append(document_entry)
|
|
238
|
+
|
|
239
|
+
# Auto-save if enabled
|
|
240
|
+
if self.auto_save:
|
|
241
|
+
self._save_current_session()
|
|
242
|
+
|
|
243
|
+
self.logger.info(f"✅ Document added to session: {document_info.get('filename', 'Unknown')}")
|
|
244
|
+
return True
|
|
245
|
+
|
|
246
|
+
except Exception as e:
|
|
247
|
+
self.logger.error(f"❌ Failed to add document to session: {e}")
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
def add_notebook(self, notebook_data: Dict[str, Any]) -> bool:
|
|
251
|
+
"""
|
|
252
|
+
Add a notebook to the current session.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
notebook_data (Dict[str, Any]): Notebook metadata (id, url, title, etc.)
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
bool: True if add successful, False otherwise
|
|
259
|
+
"""
|
|
260
|
+
try:
|
|
261
|
+
if not self.current_session:
|
|
262
|
+
self.logger.warning("No active session to add notebook to")
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
# Ensure notebooks list exists
|
|
266
|
+
if 'notebooks' not in self.current_session:
|
|
267
|
+
self.current_session['notebooks'] = []
|
|
268
|
+
|
|
269
|
+
# Add timestamp if not present
|
|
270
|
+
if 'created_at' not in notebook_data:
|
|
271
|
+
notebook_data['created_at'] = datetime.now().isoformat()
|
|
272
|
+
|
|
273
|
+
# Add notebook to session
|
|
274
|
+
self.current_session['notebooks'].append(notebook_data)
|
|
275
|
+
|
|
276
|
+
# Set as active notebook if it's the first or marked active
|
|
277
|
+
if not self.current_session.get('active_notebook_id') or notebook_data.get('active', False):
|
|
278
|
+
self.current_session['active_notebook_id'] = notebook_data.get('id')
|
|
279
|
+
|
|
280
|
+
# Auto-save if enabled
|
|
281
|
+
if self.auto_save:
|
|
282
|
+
self._save_current_session()
|
|
283
|
+
|
|
284
|
+
self.logger.info(f"✅ Notebook added to session: {notebook_data.get('id', 'Unknown')}")
|
|
285
|
+
return True
|
|
286
|
+
|
|
287
|
+
except Exception as e:
|
|
288
|
+
self.logger.error(f"❌ Failed to add notebook to session: {e}")
|
|
289
|
+
return False
|
|
290
|
+
|
|
291
|
+
def get_active_notebook(self) -> Optional[Dict[str, Any]]:
|
|
292
|
+
"""
|
|
293
|
+
Get the currently active notebook.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Optional[Dict[str, Any]]: Active notebook data or None
|
|
297
|
+
"""
|
|
298
|
+
try:
|
|
299
|
+
if not self.current_session:
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
active_id = self.current_session.get('active_notebook_id')
|
|
303
|
+
if not active_id:
|
|
304
|
+
return None
|
|
305
|
+
|
|
306
|
+
# Find notebook by ID
|
|
307
|
+
notebooks = self.current_session.get('notebooks', [])
|
|
308
|
+
for notebook in notebooks:
|
|
309
|
+
if notebook.get('id') == active_id:
|
|
310
|
+
return notebook
|
|
311
|
+
|
|
312
|
+
return None
|
|
313
|
+
|
|
314
|
+
except Exception as e:
|
|
315
|
+
self.logger.error(f"❌ Error getting active notebook: {e}")
|
|
316
|
+
return None
|
|
317
|
+
|
|
318
|
+
def set_active_notebook(self, notebook_id: str) -> bool:
|
|
319
|
+
"""
|
|
320
|
+
Set a notebook as active.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
notebook_id (str): ID of the notebook to set as active
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
bool: True if set successful, False otherwise
|
|
327
|
+
"""
|
|
328
|
+
try:
|
|
329
|
+
if not self.current_session:
|
|
330
|
+
self.logger.warning("No active session")
|
|
331
|
+
return False
|
|
332
|
+
|
|
333
|
+
# Verify notebook exists in session
|
|
334
|
+
notebooks = self.current_session.get('notebooks', [])
|
|
335
|
+
notebook_found = False
|
|
336
|
+
|
|
337
|
+
for notebook in notebooks:
|
|
338
|
+
if notebook.get('id') == notebook_id:
|
|
339
|
+
notebook_found = True
|
|
340
|
+
notebook['active'] = True
|
|
341
|
+
else:
|
|
342
|
+
notebook['active'] = False
|
|
343
|
+
|
|
344
|
+
if not notebook_found:
|
|
345
|
+
self.logger.warning(f"Notebook {notebook_id} not found in session")
|
|
346
|
+
return False
|
|
347
|
+
|
|
348
|
+
# Set as active
|
|
349
|
+
self.current_session['active_notebook_id'] = notebook_id
|
|
350
|
+
|
|
351
|
+
# Auto-save if enabled
|
|
352
|
+
if self.auto_save:
|
|
353
|
+
self._save_current_session()
|
|
354
|
+
|
|
355
|
+
self.logger.info(f"✅ Notebook set as active: {notebook_id}")
|
|
356
|
+
return True
|
|
357
|
+
|
|
358
|
+
except Exception as e:
|
|
359
|
+
self.logger.error(f"❌ Failed to set active notebook: {e}")
|
|
360
|
+
return False
|
|
361
|
+
|
|
362
|
+
def list_notebooks(self) -> List[Dict[str, Any]]:
|
|
363
|
+
"""
|
|
364
|
+
List all notebooks in the current session.
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
List[Dict[str, Any]]: List of notebook data
|
|
368
|
+
"""
|
|
369
|
+
try:
|
|
370
|
+
if not self.current_session:
|
|
371
|
+
return []
|
|
372
|
+
|
|
373
|
+
return self.current_session.get('notebooks', [])
|
|
374
|
+
|
|
375
|
+
except Exception as e:
|
|
376
|
+
self.logger.error(f"❌ Error listing notebooks: {e}")
|
|
377
|
+
return []
|
|
378
|
+
|
|
379
|
+
def get_notebook_by_id(self, notebook_id: str) -> Optional[Dict[str, Any]]:
|
|
380
|
+
"""
|
|
381
|
+
Get a specific notebook by ID.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
notebook_id (str): ID of the notebook to retrieve
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
Optional[Dict[str, Any]]: Notebook data or None if not found
|
|
388
|
+
"""
|
|
389
|
+
try:
|
|
390
|
+
notebooks = self.list_notebooks()
|
|
391
|
+
for notebook in notebooks:
|
|
392
|
+
if notebook.get('id') == notebook_id:
|
|
393
|
+
return notebook
|
|
394
|
+
|
|
395
|
+
return None
|
|
396
|
+
|
|
397
|
+
except Exception as e:
|
|
398
|
+
self.logger.error(f"❌ Error getting notebook: {e}")
|
|
399
|
+
return None
|
|
400
|
+
|
|
401
|
+
def update_notebook(self, notebook_id: str, updates: Dict[str, Any]) -> bool:
|
|
402
|
+
"""
|
|
403
|
+
Update notebook metadata.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
notebook_id (str): ID of the notebook to update
|
|
407
|
+
updates (Dict[str, Any]): Fields to update
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
bool: True if update successful, False otherwise
|
|
411
|
+
"""
|
|
412
|
+
try:
|
|
413
|
+
if not self.current_session:
|
|
414
|
+
self.logger.warning("No active session")
|
|
415
|
+
return False
|
|
416
|
+
|
|
417
|
+
notebooks = self.current_session.get('notebooks', [])
|
|
418
|
+
notebook_found = False
|
|
419
|
+
|
|
420
|
+
for notebook in notebooks:
|
|
421
|
+
if notebook.get('id') == notebook_id:
|
|
422
|
+
notebook.update(updates)
|
|
423
|
+
notebook['updated_at'] = datetime.now().isoformat()
|
|
424
|
+
notebook_found = True
|
|
425
|
+
break
|
|
426
|
+
|
|
427
|
+
if not notebook_found:
|
|
428
|
+
self.logger.warning(f"Notebook {notebook_id} not found")
|
|
429
|
+
return False
|
|
430
|
+
|
|
431
|
+
# Auto-save if enabled
|
|
432
|
+
if self.auto_save:
|
|
433
|
+
self._save_current_session()
|
|
434
|
+
|
|
435
|
+
self.logger.info(f"✅ Notebook updated: {notebook_id}")
|
|
436
|
+
return True
|
|
437
|
+
|
|
438
|
+
except Exception as e:
|
|
439
|
+
self.logger.error(f"❌ Failed to update notebook: {e}")
|
|
440
|
+
return False
|
|
441
|
+
|
|
442
|
+
def add_source_to_notebook(self, notebook_id: str, source_data: Dict[str, Any]) -> bool:
|
|
443
|
+
"""
|
|
444
|
+
Add a source to a notebook in the session.
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
notebook_id (str): ID of the notebook to add source to
|
|
448
|
+
source_data (Dict[str, Any]): Source metadata (filename, path, type, etc.)
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
bool: True if add successful, False otherwise
|
|
452
|
+
"""
|
|
453
|
+
try:
|
|
454
|
+
if not self.current_session:
|
|
455
|
+
self.logger.warning("No active session")
|
|
456
|
+
return False
|
|
457
|
+
|
|
458
|
+
# Find the notebook
|
|
459
|
+
notebook = self.get_notebook_by_id(notebook_id)
|
|
460
|
+
if not notebook:
|
|
461
|
+
self.logger.warning(f"Notebook {notebook_id} not found")
|
|
462
|
+
return False
|
|
463
|
+
|
|
464
|
+
# Ensure sources list exists
|
|
465
|
+
if 'sources' not in notebook:
|
|
466
|
+
notebook['sources'] = []
|
|
467
|
+
|
|
468
|
+
# Add timestamp and ID if not present
|
|
469
|
+
if 'added_at' not in source_data:
|
|
470
|
+
source_data['added_at'] = datetime.now().isoformat()
|
|
471
|
+
if 'source_id' not in source_data:
|
|
472
|
+
# Generate simple source ID from filename and timestamp
|
|
473
|
+
import hashlib
|
|
474
|
+
filename = source_data.get('filename', 'unknown')
|
|
475
|
+
timestamp = datetime.now().isoformat()
|
|
476
|
+
source_id = hashlib.md5(f"{filename}{timestamp}".encode()).hexdigest()[:8]
|
|
477
|
+
source_data['source_id'] = source_id
|
|
478
|
+
|
|
479
|
+
# Add source to notebook
|
|
480
|
+
notebook['sources'].append(source_data)
|
|
481
|
+
|
|
482
|
+
# Update notebook in session
|
|
483
|
+
self.update_notebook(notebook_id, {'sources': notebook['sources']})
|
|
484
|
+
|
|
485
|
+
self.logger.info(f"✅ Source added to notebook {notebook_id}: {source_data.get('filename', 'Unknown')}")
|
|
486
|
+
return True
|
|
487
|
+
|
|
488
|
+
except Exception as e:
|
|
489
|
+
self.logger.error(f"❌ Failed to add source to notebook: {e}")
|
|
490
|
+
return False
|
|
491
|
+
|
|
492
|
+
def list_notebook_sources(self, notebook_id: str) -> List[Dict[str, Any]]:
|
|
493
|
+
"""
|
|
494
|
+
List all sources for a notebook.
|
|
495
|
+
|
|
496
|
+
Args:
|
|
497
|
+
notebook_id (str): ID of the notebook
|
|
498
|
+
|
|
499
|
+
Returns:
|
|
500
|
+
List[Dict[str, Any]]: List of source data
|
|
501
|
+
"""
|
|
502
|
+
try:
|
|
503
|
+
notebook = self.get_notebook_by_id(notebook_id)
|
|
504
|
+
if not notebook:
|
|
505
|
+
return []
|
|
506
|
+
|
|
507
|
+
return notebook.get('sources', [])
|
|
508
|
+
|
|
509
|
+
except Exception as e:
|
|
510
|
+
self.logger.error(f"❌ Error listing notebook sources: {e}")
|
|
511
|
+
return []
|
|
512
|
+
|
|
513
|
+
def get_session_status(self) -> Optional[Dict[str, Any]]:
|
|
514
|
+
"""
|
|
515
|
+
Get the current session status.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
Optional[Dict[str, Any]]: Session status or None if no active session
|
|
519
|
+
"""
|
|
520
|
+
if not self.current_session:
|
|
521
|
+
return None
|
|
522
|
+
|
|
523
|
+
return {
|
|
524
|
+
'session_id': self.current_session['session_id'],
|
|
525
|
+
'ai_assistant': self.current_session['ai_assistant'],
|
|
526
|
+
'agents': self.current_session['agents'],
|
|
527
|
+
'issue_number': self.current_session['issue_number'],
|
|
528
|
+
'created_at': self.current_session['created_at'],
|
|
529
|
+
'status': self.current_session['status'],
|
|
530
|
+
'podcasts_count': len(self.current_session['podcasts_created']),
|
|
531
|
+
'documents_count': len(self.current_session['documents_processed']),
|
|
532
|
+
'notebooks_count': len(self.current_session.get('notebooks', [])),
|
|
533
|
+
'active_notebook_id': self.current_session.get('active_notebook_id'),
|
|
534
|
+
'notes_count': len(self.current_session['notes'])
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
def load_session(self, session_id: str) -> bool:
|
|
538
|
+
"""
|
|
539
|
+
Load a specific session.
|
|
540
|
+
|
|
541
|
+
Args:
|
|
542
|
+
session_id (str): ID of the session to load
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
bool: True if load successful, False otherwise
|
|
546
|
+
"""
|
|
547
|
+
try:
|
|
548
|
+
session_filename = f"session_{session_id}.json"
|
|
549
|
+
session_path = os.path.join(self.session_dir, session_filename)
|
|
550
|
+
|
|
551
|
+
if not os.path.exists(session_path):
|
|
552
|
+
self.logger.error(f"Session file not found: {session_path}")
|
|
553
|
+
return False
|
|
554
|
+
|
|
555
|
+
with open(session_path, 'r', encoding='utf-8') as f:
|
|
556
|
+
session_data = json.load(f)
|
|
557
|
+
|
|
558
|
+
self.current_session = session_data
|
|
559
|
+
self._save_current_session()
|
|
560
|
+
|
|
561
|
+
self.logger.info(f"✅ Session loaded: {session_id}")
|
|
562
|
+
return True
|
|
563
|
+
|
|
564
|
+
except Exception as e:
|
|
565
|
+
self.logger.error(f"❌ Failed to load session: {e}")
|
|
566
|
+
return False
|
|
567
|
+
|
|
568
|
+
def end_session(self) -> bool:
|
|
569
|
+
"""
|
|
570
|
+
End the current session.
|
|
571
|
+
|
|
572
|
+
Returns:
|
|
573
|
+
bool: True if end successful, False otherwise
|
|
574
|
+
"""
|
|
575
|
+
try:
|
|
576
|
+
if not self.current_session:
|
|
577
|
+
self.logger.warning("No active session to end")
|
|
578
|
+
return False
|
|
579
|
+
|
|
580
|
+
# Update session status
|
|
581
|
+
self.current_session['status'] = 'ended'
|
|
582
|
+
self.current_session['ended_at'] = datetime.now().isoformat()
|
|
583
|
+
|
|
584
|
+
# Save final session
|
|
585
|
+
self._save_current_session()
|
|
586
|
+
|
|
587
|
+
# Save to session file
|
|
588
|
+
session_filename = f"session_{self.current_session['session_id']}.json"
|
|
589
|
+
session_path = os.path.join(self.session_dir, session_filename)
|
|
590
|
+
|
|
591
|
+
with open(session_path, 'w', encoding='utf-8') as f:
|
|
592
|
+
json.dump(self.current_session, f, indent=2, ensure_ascii=False)
|
|
593
|
+
|
|
594
|
+
self.logger.info(f"✅ Session ended: {self.current_session['session_id']}")
|
|
595
|
+
|
|
596
|
+
# Clear current session
|
|
597
|
+
self.current_session = None
|
|
598
|
+
if os.path.exists(self.session_file):
|
|
599
|
+
os.remove(self.session_file)
|
|
600
|
+
|
|
601
|
+
return True
|
|
602
|
+
|
|
603
|
+
except Exception as e:
|
|
604
|
+
self.logger.error(f"❌ Failed to end session: {e}")
|
|
605
|
+
return False
|
|
606
|
+
|
|
607
|
+
def list_sessions(self) -> List[Dict[str, Any]]:
|
|
608
|
+
"""
|
|
609
|
+
List all available sessions.
|
|
610
|
+
|
|
611
|
+
Returns:
|
|
612
|
+
List[Dict[str, Any]]: List of session information
|
|
613
|
+
"""
|
|
614
|
+
sessions = []
|
|
615
|
+
|
|
616
|
+
try:
|
|
617
|
+
for file in os.listdir(self.session_dir):
|
|
618
|
+
if file.startswith('session_') and file.endswith('.json'):
|
|
619
|
+
session_path = os.path.join(self.session_dir, file)
|
|
620
|
+
|
|
621
|
+
try:
|
|
622
|
+
with open(session_path, 'r', encoding='utf-8') as f:
|
|
623
|
+
session_data = json.load(f)
|
|
624
|
+
|
|
625
|
+
# Extract summary info
|
|
626
|
+
session_summary = {
|
|
627
|
+
'session_id': session_data.get('session_id'),
|
|
628
|
+
'ai_assistant': session_data.get('ai_assistant'),
|
|
629
|
+
'issue_number': session_data.get('issue_number'),
|
|
630
|
+
'created_at': session_data.get('created_at'),
|
|
631
|
+
'status': session_data.get('status'),
|
|
632
|
+
'podcasts_count': len(session_data.get('podcasts_created', [])),
|
|
633
|
+
'documents_count': len(session_data.get('documents_processed', [])),
|
|
634
|
+
'notes_count': len(session_data.get('notes', []))
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
sessions.append(session_summary)
|
|
638
|
+
|
|
639
|
+
except Exception as e:
|
|
640
|
+
self.logger.warning(f"Error reading session file {file}: {e}")
|
|
641
|
+
|
|
642
|
+
# Sort by creation time (newest first)
|
|
643
|
+
sessions.sort(key=lambda x: x['created_at'], reverse=True)
|
|
644
|
+
|
|
645
|
+
except Exception as e:
|
|
646
|
+
self.logger.error(f"Error listing sessions: {e}")
|
|
647
|
+
|
|
648
|
+
return sessions
|
|
649
|
+
|
|
650
|
+
def _save_current_session(self):
|
|
651
|
+
"""Save the current session to file."""
|
|
652
|
+
try:
|
|
653
|
+
if self.current_session:
|
|
654
|
+
with open(self.session_file, 'w', encoding='utf-8') as f:
|
|
655
|
+
json.dump(self.current_session, f, indent=2, ensure_ascii=False)
|
|
656
|
+
except Exception as e:
|
|
657
|
+
self.logger.error(f"Error saving current session: {e}")
|
|
658
|
+
|
|
659
|
+
def _load_current_session(self):
|
|
660
|
+
"""Load the current session from file."""
|
|
661
|
+
try:
|
|
662
|
+
if os.path.exists(self.session_file):
|
|
663
|
+
with open(self.session_file, 'r', encoding='utf-8') as f:
|
|
664
|
+
self.current_session = json.load(f)
|
|
665
|
+
except Exception as e:
|
|
666
|
+
self.logger.error(f"Error loading current session: {e}")
|
|
667
|
+
|
|
668
|
+
def cleanup_old_sessions(self, days: int = 30) -> int:
|
|
669
|
+
"""
|
|
670
|
+
Clean up sessions older than specified days.
|
|
671
|
+
|
|
672
|
+
Args:
|
|
673
|
+
days (int): Number of days to keep sessions
|
|
674
|
+
|
|
675
|
+
Returns:
|
|
676
|
+
int: Number of sessions deleted
|
|
677
|
+
"""
|
|
678
|
+
deleted_count = 0
|
|
679
|
+
cutoff_time = datetime.now().timestamp() - (days * 24 * 60 * 60)
|
|
680
|
+
|
|
681
|
+
try:
|
|
682
|
+
for file in os.listdir(self.session_dir):
|
|
683
|
+
if file.startswith('session_') and file.endswith('.json'):
|
|
684
|
+
file_path = os.path.join(self.session_dir, file)
|
|
685
|
+
|
|
686
|
+
if os.path.getctime(file_path) < cutoff_time:
|
|
687
|
+
os.remove(file_path)
|
|
688
|
+
deleted_count += 1
|
|
689
|
+
|
|
690
|
+
self.logger.info(f"✅ Cleaned up {deleted_count} old sessions")
|
|
691
|
+
|
|
692
|
+
except Exception as e:
|
|
693
|
+
self.logger.error(f"Error during session cleanup: {e}")
|
|
694
|
+
|
|
695
|
+
return deleted_count
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
# Example usage and testing
|
|
699
|
+
def test_session_tracker():
|
|
700
|
+
"""Test function for session tracker."""
|
|
701
|
+
tracker = SessionTracker()
|
|
702
|
+
|
|
703
|
+
# Test session creation
|
|
704
|
+
result = tracker.start_session(ai_assistant='claude', issue_number=1)
|
|
705
|
+
print(f"Session creation result: {result}")
|
|
706
|
+
|
|
707
|
+
# Test writing to session
|
|
708
|
+
tracker.write_to_session("Test message", "note")
|
|
709
|
+
|
|
710
|
+
# Test session status
|
|
711
|
+
status = tracker.get_session_status()
|
|
712
|
+
print(f"Session status: {status}")
|
|
713
|
+
|
|
714
|
+
# Test listing sessions
|
|
715
|
+
sessions = tracker.list_sessions()
|
|
716
|
+
print(f"Found {len(sessions)} sessions")
|
|
717
|
+
|
|
718
|
+
# Test ending session
|
|
719
|
+
tracker.end_session()
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
if __name__ == "__main__":
|
|
723
|
+
test_session_tracker()
|