更新了文献分析部分

This commit is contained in:
2025-01-15 11:16:00 +00:00
parent e7311477ff
commit 1aa1f73bb8
2 changed files with 549 additions and 137 deletions
+322 -59
View File
@@ -88,6 +88,22 @@
transform: translateY(-1px);
color: white;
}
.btn-info {
background: linear-gradient(45deg, #818CF8, #6366F1);
border: none;
color: white !important;
outline: none;
}
.btn-info:hover,
.btn-info:active,
.btn-info:focus {
background: linear-gradient(45deg, #6366F1, #818CF8);
transform: translateY(-1px);
color: white !important;
outline: none;
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
}
.btn-danger {
background-color: #d3d3d3;
@@ -373,9 +389,9 @@
<!-- 项目文献页面的侧边栏 -->
<div class="sidebar-menu hidden" id="projectDetailSidebar">
<div class="sidebar-item" onclick="uploadLiterature()">
<i class="bi bi-upload"></i>
<span>Upload</span>
<div class="sidebar-item" onclick="showLiteratureList()">
<i class="bi bi-list-ul"></i>
<span>Literature List</span>
</div>
<div class="sidebar-item" onclick="downloadReferenceReport()">
<i class="bi bi-file-earmark-text"></i>
@@ -404,13 +420,37 @@
<!-- 项目文献详情页面 -->
<div id="projectDetailPage" class="content-body hidden">
<div class="content-header">
<div class="content-header d-flex justify-content-between align-items-center">
<h3 class="content-title" id="referenceAnalysisTitle">Literature Analysis</h3>
<button class="btn btn-info" onclick="uploadLiterature()">
<i class="bi bi-upload me-2"></i>
Upload
</button>
</div>
<div id="referenceAnalysis" class="mt-4">
<!-- 文献分析内容将在这里动态加载 -->
</div>
</div>
<!-- 文献分析报告页面 -->
<div id="literatureReportPage" class="content-body hidden">
<div class="content-header d-flex justify-content-between align-items-center">
<h3 class="content-title" id="literatureReportTitle">Literature Summary Report</h3>
<div class="d-flex gap-2">
<button class="btn btn-infoy" onclick="regenerateReport()">
<i class="bi bi-arrow-clockwise me-2"></i>
Regenerate
</button>
<button class="btn btn-info" onclick="downloadReport()">
<i class="bi bi-download me-2"></i>
Download
</button>
</div>
</div>
<div id="literatureReportContent" class="mt-4">
<!-- 报告内容将在这里动态加载 -->
</div>
</div>
</div>
</div>
</div>
@@ -422,6 +462,7 @@
const API_BASE_URL = 'https://dev.obscura.work/lab'; // 修改为正确的API地址
let token = localStorage.getItem('token');
let currentProjectId = null;
let currentReportData = null; // 添加存储当前报告数据的变量
// 检查登录状态
if (!token) {
@@ -520,9 +561,10 @@
document.getElementById('projectsListPage').classList.remove('hidden');
document.getElementById('projectListSidebar').classList.remove('hidden');
// 隐藏项目文献页面和对应的侧边栏
// 隐藏其他页面和侧边栏
document.getElementById('projectDetailPage').classList.add('hidden');
document.getElementById('projectDetailSidebar').classList.add('hidden');
document.getElementById('literatureReportPage').classList.add('hidden');
currentProjectId = null;
// 重新加载项目列表数据
@@ -541,6 +583,9 @@
document.getElementById('projectDetailPage').classList.remove('hidden');
document.getElementById('projectDetailSidebar').classList.remove('hidden');
// 隐藏报告页面
document.getElementById('literatureReportPage').classList.add('hidden');
document.getElementById('referenceAnalysisTitle').textContent = `Literature Analysis - ${projectName}`;
await loadReferences(projectId);
@@ -976,65 +1021,147 @@
}
try {
const loadingToast = document.createElement('div');
loadingToast.className = 'alert alert-info position-fixed top-50 start-50 translate-middle';
loadingToast.innerHTML = `
<div class="d-flex align-items-center">
<div class="spinner-border spinner-border-sm" role="status">
<span class="visually-hidden">Generating...</span>
// 1. 检查是否有文献
const referencesList = document.getElementById('referenceAnalysis');
if (referencesList && referencesList.innerHTML.includes('No literature data')) {
alert('Please upload literature first!');
return;
}
// 显示报告页面
document.getElementById('projectsListPage').classList.add('hidden');
document.getElementById('projectDetailPage').classList.add('hidden');
const reportPage = document.getElementById('literatureReportPage');
reportPage.classList.remove('hidden');
// Show loading status
const showLoading = (message) => {
document.getElementById('literatureReportContent').innerHTML = `
<div class="d-flex align-items-center justify-content-center" style="min-height: 200px;">
<div class="text-center">
<div class="spinner-border text-primary mb-3" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<h5 class="mb-0">${message}</h5>
<p class="text-muted mt-2">This may take a few minutes</p>
</div>
</div>
<span>Generating literature analysis report, please wait...</span>
</div>
`;
document.body.appendChild(loadingToast);
`;
};
const analyzeResponse = await fetch(`${API_BASE_URL}/references/${currentProjectId}/analyze_report`, {
headers: {
'Authorization': `Bearer ${token}`
}
showLoading('Getting literature analysis report...');
// 2. Check if report already exists
const existingReport = await fetch(`${API_BASE_URL}/references/${currentProjectId}/summary_report`, {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!analyzeResponse.ok) {
throw new Error((await analyzeResponse.json()).detail || 'Failed to generate report');
if (existingReport.ok) {
const report = await existingReport.json();
currentReportData = report;
document.getElementById('literatureReportContent').innerHTML = formatReportContent(report);
return;
}
const reportResponse = await fetch(`${API_BASE_URL}/references/${currentProjectId}/summary_report`, {
headers: {
'Authorization': `Bearer ${token}`
}
// If no report exists, start generation process
const generateResponse = await fetch(`${API_BASE_URL}/references/${currentProjectId}/analyze_report`, {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!reportResponse.ok) {
throw new Error((await reportResponse.json()).detail || 'Failed to get report');
if (!generateResponse.ok) {
throw new Error((await generateResponse.json()).detail || 'Failed to generate report');
}
document.body.removeChild(loadingToast);
// 3. Monitor status
const checkStatus = async () => {
const statusResponse = await fetch(`${API_BASE_URL}/references/${currentProjectId}/analysis_status`, {
headers: { 'Authorization': `Bearer ${token}` }
});
return await statusResponse.json();
};
const reportData = await reportResponse.json();
const reportText = JSON.stringify(reportData, null, 2);
const blob = new Blob([reportText], { type: 'application/json' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `literature_analysis_report_${currentProjectId}.json`;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
let attempts = 0;
const maxAttempts = 24; // 2 minutes (5 seconds × 24)
const statusCheck = setInterval(async () => {
try {
const status = await checkStatus();
attempts++;
alert('Literature analysis report generated and downloaded, you can view it in the "Reports" page');
if (status.status === 'completed') {
clearInterval(statusCheck);
const finalReport = await fetch(`${API_BASE_URL}/references/${currentProjectId}/summary_report`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const report = await finalReport.json();
currentReportData = report;
document.getElementById('literatureReportContent').innerHTML = formatReportContent(report);
}
else if (status.status === 'failed') {
clearInterval(statusCheck);
throw new Error(status.error || 'Report generation failed');
}
else if (attempts >= maxAttempts) {
clearInterval(statusCheck);
document.getElementById('literatureReportContent').innerHTML = `
<div class="alert alert-warning">
Report generation is taking longer than expected. Please refresh and try again later.
<button class="btn btn-primary mt-2" onclick="downloadReferenceReport()">
Refresh
</button>
</div>
`;
}
else {
showLoading(`${status.status === 'processing' ? 'Generating report' : 'Waiting'}...`);
}
} catch (error) {
clearInterval(statusCheck);
document.getElementById('literatureReportContent').innerHTML = `
<div class="alert alert-danger">
Failed to generate report: ${error.message}
<button class="btn btn-primary mt-2" onclick="downloadReferenceReport()">
Retry
</button>
</div>
`;
}
}, 5000);
} catch (error) {
const loadingToast = document.querySelector('.alert-info');
if (loadingToast) {
document.body.removeChild(loadingToast);
}
console.error('Failed to generate literature analysis report:', error);
alert(`Failed to generate literature analysis report: ${error.message}`);
document.getElementById('literatureReportContent').innerHTML = `
<div class="alert alert-danger">
Operation failed: ${error.message}
<button class="btn btn-primary mt-2" onclick="downloadReferenceReport()">
Retry
</button>
</div>
`;
}
}
// 添加格式化报告内容的函数
function formatReportContent(jsonData) {
if (!jsonData) return '<div class="alert alert-info">No report data available</div>';
return `<div class="card">
<div class="card-body">
<pre style="white-space: pre-wrap; word-wrap: break-word;">${
JSON.stringify(jsonData, null, 2)
.replace(/[{}"]/g, '')
.replace(/},/g, '')
.replace(/{ },/g, '')
.replace(/^\s*,/gm, '')
.replace(/Literature Summary Report:/, '<div style="text-align: center"><strong style="font-size: 24px;">Literature Summary Report</strong></div>')
.replace(/^(\s*\d+\.[^:\n]+:)/gm, '<strong>$1</strong>')
.split('\n')
.map(line => line.trimEnd())
.join('\n')
}</pre>
</div>
</div>`;
}
// Delete literature function
async function deleteReference(projectId, referenceId) {
if (!confirm('Are you sure you want to delete this literature? This action cannot be undone.')) {
@@ -1357,19 +1484,155 @@
}
}
// 添加一些基本的样式
const style = document.createElement('style');
style.textContent = `
pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
// 添加显示文献列表的函数
function showLiteratureList() {
// 隐藏报告页面
document.getElementById('literatureReportPage').classList.add('hidden');
// 显示文献列表页面
document.getElementById('projectDetailPage').classList.remove('hidden');
// 如果有当前项目ID,重新加载文献列表
if (currentProjectId) {
loadReferences(currentProjectId);
}
code {
font-family: Consolas, Monaco, 'Andale Mono', monospace;
}
// 添加下载报告功能
function downloadReport() {
if (!currentReportData) {
alert('No report available to download');
return;
}
`;
document.head.appendChild(style);
try {
// 使用原始的JSON数据创建下载内容
const downloadContent = JSON.stringify(currentReportData, null, 2);
// 创建Blob对象
const blob = new Blob([downloadContent], { type: 'application/json' });
const url = window.URL.createObjectURL(blob);
// 创建临时下载链接
const a = document.createElement('a');
a.href = url;
a.download = `literature_analysis_report_${new Date().toISOString().split('T')[0]}.json`;
// 触发下载
document.body.appendChild(a);
a.click();
// 清理
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('Failed to download report:', error);
alert('Failed to download report');
}
}
// 添加重新生成报告功能
async function regenerateReport() {
if (!currentProjectId) {
alert('Please select a project first');
return;
}
if (!confirm('Are you sure you want to regenerate the report? This may take a few minutes.')) {
return;
}
try {
// 显示加载状态
const showLoading = (message) => {
document.getElementById('literatureReportContent').innerHTML = `
<div class="d-flex align-items-center justify-content-center" style="min-height: 200px;">
<div class="text-center">
<div class="spinner-border text-primary mb-3" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<h5 class="mb-0">${message}</h5>
<p class="text-muted mt-2">This may take a few minutes</p>
</div>
</div>
`;
};
showLoading('Regenerating report...');
// 直接调用重新生成API
const generateResponse = await fetch(`${API_BASE_URL}/references/${currentProjectId}/analyze_report?force=true`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!generateResponse.ok) {
throw new Error((await generateResponse.json()).detail || 'Failed to regenerate report');
}
// 监控状态
let attempts = 0;
const maxAttempts = 24; // 2 minutes (5 seconds × 24)
const statusCheck = setInterval(async () => {
try {
const statusResponse = await fetch(`${API_BASE_URL}/references/${currentProjectId}/analysis_status`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const status = await statusResponse.json();
attempts++;
if (status.status === 'completed') {
clearInterval(statusCheck);
const finalReport = await fetch(`${API_BASE_URL}/references/${currentProjectId}/summary_report`, {
headers: { 'Authorization': `Bearer ${token}` }
});
const report = await finalReport.json();
currentReportData = report;
document.getElementById('literatureReportContent').innerHTML = formatReportContent(report);
}
else if (status.status === 'failed') {
clearInterval(statusCheck);
throw new Error(status.error || 'Report regeneration failed');
}
else if (attempts >= maxAttempts) {
clearInterval(statusCheck);
document.getElementById('literatureReportContent').innerHTML = `
<div class="alert alert-warning">
Report regeneration is taking longer than expected. Please refresh and try again later.
<button class="btn btn-primary mt-2" onclick="regenerateReport()">
Retry
</button>
</div>
`;
}
else {
showLoading(`${status.status === 'processing' ? 'Regenerating report' : 'Waiting'}...`);
}
} catch (error) {
clearInterval(statusCheck);
document.getElementById('literatureReportContent').innerHTML = `
<div class="alert alert-danger">
Failed to regenerate report: ${error.message}
<button class="btn btn-primary mt-2" onclick="regenerateReport()">
Retry
</button>
</div>
`;
}
}, 5000);
} catch (error) {
document.getElementById('literatureReportContent').innerHTML = `
<div class="alert alert-danger">
Operation failed: ${error.message}
<button class="btn btn-primary mt-2" onclick="regenerateReport()">
Retry
</button>
</div>
`;
}
}
</script>
</body>
</html>
</html>
+227 -78
View File
@@ -27,6 +27,7 @@ from io import BytesIO, StringIO
import aiohttp
from concurrent.futures import ThreadPoolExecutor
from functools import partial
import threading
# Database Configuration
MONGODB_URL = "mongodb://lab:[email protected]:27017/lab"
@@ -1275,7 +1276,7 @@ async def export_experiment_data(
experiment = await db.experiments.find_one({"_id": ObjectId(experiment_id)})
zip_filename = f"experiment_{experiment['experiment_name']}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
# 准备ZIP文��下载
# 准备ZIP文<50><E69687>下载
zip_buffer.seek(0)
return StreamingResponse(
@@ -1649,68 +1650,6 @@ async def analyze_project_data(project_data):
return None
# 修改函数定义为异步函数
async def analyze_reference_summary(reference_data):
system_prompt = """
You are an AI assistant responsible for analyzing literature reports.
You will summarize and analyze all literature reports and generate a comprehensive analysis report in JSON format.
The JSON structure must strictly follow the provided template.
"""
user_prompt = f"""Analyze the following literature reports:
Literature reports: {json.dumps(reference_data, ensure_ascii=False)}
Generate a JSON response with the following structure:
{{
"Literature Summary Report": {{
"overview": {{
"total_papers": "[Number of papers]",
"time_range": {{"start_year": "[Start year]", "end_year": "[End year]"}},
"main_research_areas": "[Main research areas of the papers]"
}},
"research_trends": {{
"major_themes": "[Major themes areas of the papers]",
"common_methodologies": "[Common methodologies used in the papers]",
"emerging_topics": "[Emerging topics in the papers]"
}},
"key_findings": {{
"theoretical_advances": "[Theoretical advances in the papers]",
"experimental_results": "[Experimental results in the papers]",
"common_conclusions": "[Common conclusions in the papers]"
}},
"research_gaps": {{
"current_limitations": "[Current limitations]",
"unexplored_areas": "[Unexplored areas]",
"technical_challenges": "[Technical challenges"
}},
"future_directions": {{
"potential_applications": "[Potential future applications]",
"methodological_suggestions": "[Methodological suggestions based on literature summary]"
}},
"impact_assessment": {{
"academic_influence": "[Summarize the academic influence of the literature]",
"practical_value": "[Summarize the practical value of the literature]"
}}
}}
}}
"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
try:
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages,
response_format={'type': 'json_object'}
)
return json.loads(response.choices[0].message.content)
except Exception as e:
print(f"Error calling DeepSeek API: {e}")
return None
@app.get("/lab/projects/{project_id}/analyze")
@@ -2116,6 +2055,75 @@ async def get_reference_report(
except Exception as e:
print(f"Error closing Redis connection: {e}")
# 修改函数定义为异步函数
async def analyze_reference_summary(reference_data):
system_prompt = """
You are an AI assistant responsible for analyzing literature reports.
You will summarize and analyze all literature reports and generate a comprehensive analysis report in JSON format.
The JSON structure must strictly follow the provided template.
"""
user_prompt = f"""Analyze the following literature reports:
Literature reports: {json.dumps(reference_data, ensure_ascii=False)}
Generate a JSON response with the following structure:
{{
"Literature Summary Report": {{
"overview": {{
"total_papers": "[Number of papers]",
"time_range": {{"start_year": "[Start year]", "end_year": "[End year]"}},
"main_research_areas": "[Main research areas of the papers]"
}},
"research_trends": {{
"major_themes": "[Major themes areas of the papers]",
"common_methodologies": "[Common methodologies used in the papers]",
"emerging_topics": "[Emerging topics in the papers]"
}},
"key_findings": {{
"theoretical_advances": "[Theoretical advances in the papers]",
"experimental_results": "[Experimental results in the papers]",
"common_conclusions": "[Common conclusions in the papers]"
}},
"research_gaps": {{
"current_limitations": "[Current limitations]",
"unexplored_areas": "[Unexplored areas]",
"technical_challenges": "[Technical challenges"
}},
"future_directions": {{
"potential_applications": "[Potential future applications]",
"methodological_suggestions": "[Methodological suggestions based on literature summary]"
}},
"impact_assessment": {{
"academic_influence": "[Summarize the academic influence of the literature]",
"practical_value": "[Summarize the practical value of the literature]"
}}
}}
}}
"""
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
try:
response = client.chat.completions.create(
model="deepseek-chat",
messages=messages,
response_format={'type': 'json_object'}
)
return json.loads(response.choices[0].message.content)
except Exception as e:
print(f"Error calling DeepSeek API: {e}")
return None
# 创建线程池
analysis_thread_pool = ThreadPoolExecutor(max_workers=3)
@app.get("/lab/references/{project_id}/analyze_report")
async def analyze_reference_summary_report(
project_id: str,
@@ -2126,6 +2134,21 @@ async def analyze_reference_summary_report(
redis = await get_redis()
try:
# 检查是否已经有正在进行的分析任务
await redis.select(204)
status_key = f"reference_analysis_status:{project_id}"
current_status = await redis.get(status_key)
if current_status:
status_data = json.loads(current_status)
if status_data.get("status") == "processing":
return {
"message": "文献分析任务正在进行中",
"status": "processing",
"project_id": project_id,
"start_time": status_data.get("start_time")
}
# 从MongoDB查找项目下的所有实验ID
reference_cursor = db.references.find({
"project_id": ObjectId(project_id)
@@ -2138,9 +2161,62 @@ async def analyze_reference_summary_report(
if not reference_ids:
raise HTTPException(status_code=404, detail="No references found for this project")
# 记录分析开始状态
status_data = {
"status": "processing",
"start_time": datetime.now(timezone.utc).isoformat(),
"total_references": len(reference_ids),
"completed_references": 0
}
await redis.set(status_key, json.dumps(status_data))
# 在新线程中运行分析任务
analysis_thread_pool.submit(run_analysis_in_thread, project_id, reference_ids)
return {
"message": "文献分析任务已启动",
"status": "processing",
"project_id": project_id,
"start_time": status_data["start_time"],
"total_references": len(reference_ids)
}
except Exception as e:
print(f"Error starting reference analysis: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
finally:
try:
await redis.aclose()
except Exception as e:
print(f"Error closing Redis connection: {e}")
def run_analysis_in_thread(project_id: str, reference_ids: List[str]):
"""在独立线程中运行分析任务"""
# 创建新的事件循环
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
# 在新的事件循环中运行异步任务
loop.run_until_complete(process_reference_analysis(project_id, reference_ids))
except Exception as e:
print(f"Error in analysis thread: {str(e)}")
finally:
loop.close()
async def process_reference_analysis(project_id: str, reference_ids: List[str]):
"""后台处理文献分析任务"""
redis = await get_redis()
try:
# 更新状态键
await redis.select(204)
status_key = f"reference_analysis_status:{project_id}"
# 从Redis db203获取所有实验报告
await redis.select(203)
all_reference_reports = {} # 修改为字典,使用实验ID作为key
all_reference_reports = {}
completed_count = 0
for ref_id in reference_ids:
report_key = f"reference_report:{ref_id}"
@@ -2149,28 +2225,101 @@ async def analyze_reference_summary_report(
if report_data:
try:
parsed_report = json.loads(report_data)
all_reference_reports[ref_id] = parsed_report # 使用实验ID作为key存储报告
all_reference_reports[ref_id] = parsed_report
completed_count += 1
# 更新进度
await redis.select(204)
current_status = await redis.get(status_key)
if current_status:
status_data = json.loads(current_status)
status_data["completed_references"] = completed_count
await redis.set(status_key, json.dumps(status_data))
await redis.select(203)
except json.JSONDecodeError:
print(f"Error parsing reference {ref_id} report")
continue
if not all_reference_reports:
raise HTTPException(status_code=404, detail="No reference report data found")
# 发送所有实验报告进行项目级分析
analysis_result = await analyze_reference_summary(all_reference_reports) # 传递带有ID的报告字典
if not analysis_result:
raise HTTPException(status_code=500, detail="Failed to generate project analysis report")
# 将分析结果保存到Redis db202
if all_reference_reports:
analysis_result = await analyze_reference_summary(all_reference_reports)
if analysis_result:
await redis.select(204)
report_key = f"reference_summary_report:{project_id}"
await redis.set(report_key, json.dumps(analysis_result))
# 更新最终状态
status_data = {
"status": "completed",
"completion_time": datetime.now(timezone.utc).isoformat(),
"total_references": len(reference_ids),
"completed_references": completed_count
}
await redis.set(status_key, json.dumps(status_data))
print(f"Reference analysis completed for project {project_id}")
else:
# 更新失败状态
status_data = {
"status": "failed",
"error": "Failed to generate analysis result",
"completion_time": datetime.now(timezone.utc).isoformat()
}
await redis.set(status_key, json.dumps(status_data))
print(f"Failed to generate analysis for project {project_id}")
else:
# 更新失败状态
status_data = {
"status": "failed",
"error": "No valid reference reports found",
"completion_time": datetime.now(timezone.utc).isoformat()
}
await redis.set(status_key, json.dumps(status_data))
print(f"No valid reference reports found for project {project_id}")
except Exception as e:
print(f"Error in background reference analysis: {str(e)}")
try:
await redis.select(204)
status_data = {
"status": "failed",
"error": str(e),
"completion_time": datetime.now(timezone.utc).isoformat()
}
await redis.set(status_key, json.dumps(status_data))
except Exception as redis_error:
print(f"Error updating Redis status: {redis_error}")
finally:
try:
await redis.aclose()
except Exception as e:
print(f"Error closing Redis connection: {e}")
# 添加新的状态检查路由
@app.get("/lab/references/{project_id}/analysis_status")
async def get_reference_analysis_status(
project_id: str,
current_user: UserModel = Depends(get_current_user)
):
"""获取文献分析任务的状态"""
redis = await get_redis()
try:
await redis.select(204)
report_key = f"reference_summary_report:{project_id}"
await redis.set(report_key, json.dumps(analysis_result))
status_key = f"reference_analysis_status:{project_id}"
status_data = await redis.get(status_key)
return analysis_result
if not status_data:
return {
"status": "not_started",
"project_id": project_id
}
return json.loads(status_data)
except Exception as e:
print(f"Error analyzing project data: {str(e)}")
print(f"Error getting analysis status: {str(e)}")
raise HTTPException(status_code=500, detail=str(e))
finally:
try: