初始提交
This commit is contained in:
Executable
+196
@@ -0,0 +1,196 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
方法学提取 API - FastAPI 版本
|
||||
|
||||
提供接口:
|
||||
- POST /extract - 从 PDF 提取方法学参数
|
||||
- GET /result/{job_id} - 获取提取结果
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from fastapi import FastAPI, File, UploadFile, HTTPException
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from src.rag.extractors.methodology.simple_extractor import SimpleExtractor
|
||||
|
||||
app = FastAPI(title="Methodology Extraction API")
|
||||
|
||||
# CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# 存储提取结果
|
||||
results = {}
|
||||
|
||||
|
||||
class ExtractionResult(BaseModel):
|
||||
job_id: str
|
||||
status: str
|
||||
data: Optional[Dict] = None
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
@app.post("/extract")
|
||||
async def extract_methodology(pdf: UploadFile = File(...)):
|
||||
"""从 PDF 提取方法学参数"""
|
||||
|
||||
# 生成 job_id
|
||||
job_id = str(uuid.uuid4())[:8]
|
||||
|
||||
# 保存 PDF
|
||||
upload_dir = Path(__file__).parent.parent / 'uploads'
|
||||
upload_dir.mkdir(exist_ok=True)
|
||||
pdf_path = upload_dir / f"{job_id}_{pdf.filename}"
|
||||
|
||||
with open(pdf_path, "wb") as f:
|
||||
content = await pdf.read()
|
||||
f.write(content)
|
||||
|
||||
# 提取
|
||||
try:
|
||||
extractor = SimpleExtractor()
|
||||
result_text = extractor.extract(pdf_path)
|
||||
|
||||
# 解析为结构化数据
|
||||
structured = parse_extraction_result(result_text)
|
||||
|
||||
# 保存结果
|
||||
results[job_id] = {
|
||||
'status': 'completed',
|
||||
'pdf_path': str(pdf_path),
|
||||
'raw_text': result_text,
|
||||
'data': structured
|
||||
}
|
||||
|
||||
return {
|
||||
'job_id': job_id,
|
||||
'status': 'completed',
|
||||
'data': structured
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
results[job_id] = {
|
||||
'status': 'failed',
|
||||
'error': str(e)
|
||||
}
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@app.get("/result/{job_id}")
|
||||
async def get_result(job_id: str):
|
||||
"""获取提取结果"""
|
||||
if job_id not in results:
|
||||
raise HTTPException(status_code=404, detail="Job not found")
|
||||
|
||||
return results[job_id]
|
||||
|
||||
|
||||
def parse_extraction_result(text: str) -> Dict:
|
||||
"""解析提取结果为结构化数据"""
|
||||
|
||||
data = {
|
||||
'species': {
|
||||
'target': '',
|
||||
'references': []
|
||||
},
|
||||
'gene_family': {
|
||||
'name': '',
|
||||
'pfam_id': ''
|
||||
},
|
||||
'stages': []
|
||||
}
|
||||
|
||||
# 简单解析
|
||||
lines = text.split('\n')
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
|
||||
# 物种
|
||||
if '目标物种' in line or 'target' in line.lower():
|
||||
if ':' in line:
|
||||
data['species']['target'] = line.split(':', 1)[-1].strip()
|
||||
elif '参考物种' in line or 'references' in line.lower():
|
||||
if ':' in line:
|
||||
refs = line.split(':', 1)[-1].strip()
|
||||
data['species']['references'] = [r.strip() for r in refs.split('、') if r.strip()]
|
||||
elif '基因家族' in line:
|
||||
if ':' in line:
|
||||
parts = line.split(':', 1)[-1].strip()
|
||||
if '(' in parts and 'Pfam:' in parts:
|
||||
name = parts.split('(')[0].strip()
|
||||
pfam = parts.split('Pfam:')[-1].split(')')[0].strip()
|
||||
data['gene_family']['name'] = name
|
||||
data['gene_family']['pfam_id'] = pfam
|
||||
|
||||
# 默认阶段
|
||||
data['stages'] = [
|
||||
{
|
||||
'id': 'identify',
|
||||
'name': '1. 基因家族鉴定',
|
||||
'params': [
|
||||
{'name': 'tool', 'label': '工具', 'value': 'BLASTp + HMMER'},
|
||||
{'name': 'pfam_id', 'label': 'Pfam ID', 'value': 'PF03110'},
|
||||
{'name': 'e_value', 'label': 'E-value', 'value': ''},
|
||||
{'name': 'identity', 'label': 'Identity', 'value': ''}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'phylogeny',
|
||||
'name': '2. 系统发育分析',
|
||||
'params': [
|
||||
{'name': 'software', 'label': '软件', 'value': 'MEGA 6.0'},
|
||||
{'name': 'method', 'label': '方法', 'value': 'Neighbor-Joining'},
|
||||
{'name': 'bootstrap', 'label': 'Bootstrap', 'value': '1000'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'motif',
|
||||
'name': '3. 保守基序分析',
|
||||
'params': [
|
||||
{'name': 'tool', 'label': '工具', 'value': 'MEME'},
|
||||
{'name': 'max_motifs', 'label': 'Max motifs', 'value': '10'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'structure',
|
||||
'name': '4. 基因结构分析',
|
||||
'params': [
|
||||
{'name': 'tool', 'label': '工具', 'value': 'GSDS'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'synteny',
|
||||
'name': '5. 共线性分析',
|
||||
'params': [
|
||||
{'name': 'tool', 'label': '工具', 'value': 'MCScanX'}
|
||||
]
|
||||
},
|
||||
{
|
||||
'id': 'expression',
|
||||
'name': '6. 表达分析',
|
||||
'params': [
|
||||
{'name': 'method', 'label': '方法', 'value': 'qRT-PCR'}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
return data
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import uvicorn
|
||||
uvicorn.run(app, host="0.0.0.0", port=5001)
|
||||
Reference in New Issue
Block a user