Skip to content

since25/EditR_wang

Repository files navigation

EditR

EditR is a base editing analysis platform for Sanger .ab1 files, featuring a modern Next.js frontend and an R plumber REST API backend.

Current app version: 2.0.0

Architecture

┌──────────────────────┐   HTTP / JSON    ┌──────────────────────┐
│   Next.js 前端        │ ◄──────────────► │   R plumber API       │
│  (React + Tailwind)   │                  │   (REST endpoints)    │
│  :3000                │                  │   :8000 (本地)        │
└──────────────────────┘                  │   :8001 (生产 systemd)│
                                           └──────────┬───────────┘
                                                      │
                              ┌───────────────────────┼────────────────────────┐
                              ▼                        ▼                        ▼
                       核心分析 (R/)            会话临时存储              SQLite DB
                  analysis.R / data_*.R      tempdir()/editr_uploads   模板 / 正则预设
  • 后端api.R(plumber)暴露 REST 端点,分析逻辑在 R/analysis.RR/data_processing.R,持久化在 R/database.R(SQLite)。
  • 前端frontend/(Next.js),所有后端交互封装在 frontend/src/lib/api.ts
  • 会话(session):上传文件后服务端按 session_id.ab1 存到临时目录,后续分析 / 下载都用这个 session_id 引用,不必重复上传。

序列化注意:plumber 默认把标量序列化成单元素数组,例如 {"session_id": ["20260530_1234"]}。调用方读取标量时要解包(取数组第 0 项);回传 session_id 时数组或字符串后端都能接受。

调用结构与方法(新版核心流程)

一次完整分析按 上传 → 分析 → 预生成 → 下载 四步走,全部围绕一个 session_id

1. 上传        POST /api/upload/file   ──► 得到 session_id(多文件用同一个 session_id)
2. 分析        POST /api/analyze       ──► 返回 summary/图表数据;结果按 session_id 缓存在服务端
                                            (重命名在这一步前置应用,Table_Name 即为重命名后的名字)
3. 预生成(可选) POST /api/results/prepare ──► 服务端立即打包 ZIP 并缓存,使下载可秒下
4. 下载        POST /api/download/results ──► 流式返回 ZIP(命中缓存则无需重新分析)

关键设计:

  • 重命名前置rename_config 在第 2 步 /api/analyze 就提交,后端先算好 原始名 → 重命名名 映射,分析结果、网页图表、Excel 摘要、打包文件名全程一致。
  • 结果缓存/api/analyze 把分析结果按 session_id 缓存;/api/results/prepare/api/download/results 复用缓存,下载时不再重新分析
  • 分类打包:结果 ZIP 内分三个子目录 —— ab1_files/(重命名后的 .ab1,带 _success/_failed 状态后缀)、charts/(位点图 + 热图 PNG)、analysis_summary/(多 Sheet Excel)。
  • 分组(grouping,可选):分析后可在网页给每个样品填 条件(Condition) + 重复组(Replicate)(支持粘贴 序号 条件 重复组 文本批量录入)。分组随 prepare/download 提交:Excel 的 summary 增列并新增 grouping sheet,charts/ 增加 grouped_bar.png(并排重复)、grouped_mean.png(均值±SD)、heatmap_grouped.png(按组重排)。分组变化时结果 ZIP 自动重新打包。
  • MSG guide 对齐(可选):若实验使用多条 MSG 序列(如 MSG-GA-1 到 MSG-GA-5),在「MSG Guide 序列」面板配置每个 condition 的 guide 序列 + 参考序列片段,重新分析时后端用各样品正确的 guide 计算。高阶可视化(分组图 / 序列对齐 / 跨批次对比)在独立的 /report 报告页呈现,数据源为 Grist。
  • 服务端归档(可选):分析后点「归档」,把原始 AB1 + 完整结果包 + 参数持久化到服务器,按「项目/批次」组织;/report 可浏览、下载、「载入重分析」(无需重新上传即可换 guide 重跑)。

MSG 多序列对齐视图使用说明

当一个实验中用到多条 MSG guide 序列(位置略有不同,均覆盖同一目标碱基)时,可以利用「序列对齐视图」在绝对参考坐标下直观对比不同 guide 的编辑窗口和效率,区分「窗口偏移」与「偏好性变化」的贡献。

完整使用流程

Step 1:初次分析

正常上传 AB1 文件,用默认 guide(如 MSG-GA-1)配置「序列输入」并点「开始分析」。得到样品列表和初步结果。

Step 2:分配分组信息

在「分组信息」面板给每个样品填写 Condition(建议包含 MSG 编号,如 44C msg-GA-1)和 Replicate(如 U1U2)。支持粘贴批量录入:

1  44C msg-GA-1  U1
2  44C msg-GA-2  U1
3  44C msg-GA-3  U1
4  44C msg-GA-4  U1
5  44C msg-GA-5  U1
6  44C msg-GA-1  U2
...

Step 3:填写 MSG Guide 序列面板

点击展开「MSG Guide 序列(用于对齐视图)」面板,填写两项:

  1. 参考序列片段:包含所有 guide 覆盖区域的短序列,目标碱基用大写字母标出,其余小写。例如:

    ctaacccccacccccactgccGggtaagggtgtc
    
  2. 每个 condition 对应的 guide 序列(5′→3′):在表格中逐行填写,或粘贴如下格式一次性导入:

    44C msg-GA-1  TTACCCGGCAGTGGGGGTGGGGG
    44C msg-GA-2  ATACCCGGCAGTGGGGGTGGGGG
    44C msg-GA-3  GAACCCGGCAGTGGGGGTGGGGG
    44C msg-GA-4  CGACCCGGCAGTGGGGGTGGGGG
    44C msg-GA-5  TGCCCGGCAGTGGGGGTGGGGGC
    

    (末列为纯 ACGT guide 序列,前面为 condition 名;正链/负链均可,系统自动识别)

Step 4:用正确 guide 重新分析

当分组和 MSG guide 序列填好后,再次点「开始分析」。后端会为每个样品自动使用对应 condition 的 guide 序列进行 EditR 计算,Summary 表中的目标位点编辑率精确到正确的 guide.position。

Step 5:push 到 Grist

展开「Grist 同步」面板,填批次标签,点「同步到 Grist」。结果(含每位点剖面 + 参考序列)写入 Grist,供报告页使用。

Step 6:在「报告 / 归档页」查看序列对齐与跨批次对比

高阶可视化已独立成 /report(顶部「报告 / 归档 →」进入),核心分析页回归纯分析。

/report

  • 选一个批次 → 「加载并出图」→ 切「序列对齐」tab,看到:
    • X 轴 = 参考序列绝对坐标(1 基);每行 = 一个 condition(重复均值);柱高 = 编辑效率
    • 橙色高亮柱/虚线 = 目标碱基位置;每行下方彩色带 = 该 guide 覆盖范围及方向(正链 负链);最底行 = 参考序列碱基
  • 多选第二个批次 → 灰色半透明叠加对比;或「导入快照对比」加载历史 JSON 快照

服务端归档(持久化 + 可重分析)

分析后在下载区点「归档」,填 项目 / 批次 / 注释,把原始 AB1 + 完整结果包 + 参数 manifest 持久化到服务器 ~/.local/share/EditR_wang/archive/<项目>/<批次>/。在 /report 的「归档浏览」可:

  • 下载 归档的完整结果包
  • 载入重分析:把归档原始 AB1 载回新会话并跳分析页预载,换 guide/参数重跑(无需重新上传文件)

/api/analyze 新增参数

参数 类型 说明
guide_overrides [{table_name: string, guide: string}] 每个样品(按重命名后 Table_Name)使用的 guide 序列;前端从 grouping + MSG guide 面板自动计算并提交
ref_sequence string 可选参考序列片段(ACGT,大小写均可);提供时 target_position 从 guide-relative 坐标自动转换为参考序列绝对坐标,输出列名如 N21_edit_rate 表示参考序列第 21 位

Quick Start

1. 启动后端 API

# 安装 R 依赖(CRAN + Bioconductor,含 pwalign)
Rscript dependencies.R

# 启动 API(默认端口 8000,可用环境变量覆盖)
Rscript start_api.R
# EDITR_API_PORT=8001 EDITR_API_HOST=0.0.0.0 Rscript start_api.R

Swagger 文档:http://localhost:8000/__docs__/

2. 启动前端

cd frontend
npm install
npm run dev          # 开发:http://localhost:3000
# 生产:npm run build && npx next start -p 3000

前端默认请求的后端地址在 frontend/src/lib/api.tsAPI_BASE),按部署环境调整。

API 端点参考

方法 端点 描述
GET /api/health 健康检查,返回版本与 DB 状态
POST /api/upload/file?filename=NAME 上传单个 .ab1(二进制 body);可带 x-session-id 头追加到已有会话
POST /api/upload/zip?filename=NAME 上传 ZIP,自动解压提取其中的 .ab1
POST /api/analyze 批量分析已上传文件(JSON body)
POST /api/results/prepare 预生成结果 ZIP 并缓存(JSON body)
POST /api/download/results 下载完整结果 ZIP(JSON body,流式 application/zip)
POST /api/download/excel 仅下载 Excel(JSON body)
GET /api/templates 获取序列模板列表
POST /api/templates 保存序列模板(name, sequence, target_position
DELETE /api/templates?id=ID 删除序列模板
GET /api/regex 获取已保存的正则预设
POST /api/regex 保存正则预设(name, pattern, replacement
DELETE /api/regex?id=ID 删除正则预设
GET /api/rename/presets 获取内置正则预设
POST /api/rename/preview 预览文件重命名结果

请求 / 响应形态(主要端点)

POST /api/upload/file?filename=sample.ab1 二进制 body = 文件字节;首次返回新的 session_id,后续文件带 x-session-id 头复用:

// → 响应
{ "success": [true], "session_id": ["20260530_1234"], "filename": ["sample.ab1"], "size": [271097] }

POST /api/analyzeContent-Type: application/json

// ← 请求体
{
  "session_id": "20260530_1234",
  "sequence": "TTACCCGGCAGTGGGGGTGGGGG",  // 参考序列,仅 ACGT
  "target_position": 6,                     // 目标位置(1 基)
  "target_base": "C",                       // 目标碱基
  "edited_base": "T",                       // 编辑后碱基
  "template_name": "RD-103",                // 可选,用于结果包命名
  "rename_config": {                         // 可选,重命名规则(前置应用)
    "preset": "saved_1",                    // ""=不重命名 | 内置预设键 | "saved_<id>" | "custom"
    "pattern": "\\.\\d{7}\\..+",            // preset=custom/saved 时的正则
    "replacement": "",
    "tag": "rep1"                           // 追加到样本名的标签(可选)
  }
}

// → 响应
{
  "success": [true],
  "data": {
    "summary":      [ /* 每样本目标位点编辑率等 */ ],
    "per_position": [ /* 各位点编辑率 */ ],
    "heatmap":      [ /* 热图长表数据 */ ],
    "raw":          [ /* 原始指标表 */ ],
    "n_files": [2], "n_success": [2],
    "processing_mode": ["sequential"], "elapsed_seconds": [1.2],
    "errors": []   // 失败文件清单;无失败时为 [](数组)
  }
}

POST /api/results/prepare → 分析成功后立即调用,使下载秒下

// ← { "session_id": "20260530_1234" }
// → { "success": [true], "ready": [true], "filename": ["results_RD-103_20260530.zip"] }

POST /api/download/results → 返回 ZIP 二进制(application/zip

// ← { "session_id": "20260530_1234" }
//   (若服务端结果缓存已失效,可在 body 里附带完整分析参数作兜底,会重新分析)

端到端示例

curl

BASE=http://localhost:8000

# 1. 上传,拿到 session_id
SID=$(curl -s -X POST "$BASE/api/upload/file?filename=sample.ab1" \
        -H "Content-Type: application/octet-stream" \
        --data-binary @sample.ab1 | python3 -c "import sys,json;v=json.load(sys.stdin)['session_id'];print(v[0] if isinstance(v,list) else v)")

# 2. 分析(带重命名)
curl -s -X POST "$BASE/api/analyze" -H "Content-Type: application/json" -d "{
  \"session_id\": \"$SID\", \"sequence\": \"TTACCCGGCAGTGGGGGTGGGGG\",
  \"target_position\": 6, \"target_base\": \"C\", \"edited_base\": \"T\",
  \"template_name\": \"RD-103\",
  \"rename_config\": {\"preset\": \"custom\", \"pattern\": \"\\\\.\\\\d{7}\\\\..+\", \"replacement\": \"\", \"tag\": \"rep1\"}
}"

# 3. 预生成结果包
curl -s -X POST "$BASE/api/results/prepare" -H "Content-Type: application/json" -d "{\"session_id\": \"$SID\"}"

# 4. 下载 ZIP
curl -s -X POST "$BASE/api/download/results" -H "Content-Type: application/json" \
     -d "{\"session_id\": \"$SID\"}" -o results.zip

Python

import requests

BASE = "http://localhost:8000"

# 1. 上传
r = requests.post(f"{BASE}/api/upload/file", params={"filename": "sample.ab1"},
                  data=open("sample.ab1", "rb").read(),
                  headers={"Content-Type": "application/octet-stream"})
sid = r.json()["session_id"]
sid = sid[0] if isinstance(sid, list) else sid   # 解包标量数组

# 2. 分析
res = requests.post(f"{BASE}/api/analyze", json={
    "session_id": sid,
    "sequence": "TTACCCGGCAGTGGGGGTGGGGG",
    "target_position": 6, "target_base": "C", "edited_base": "T",
    "template_name": "RD-103",
}).json()
print(res["data"]["summary"])

# 3. 预生成 + 4. 下载
requests.post(f"{BASE}/api/results/prepare", json={"session_id": sid})
zip_bytes = requests.post(f"{BASE}/api/download/results", json={"session_id": sid}).content
open("results.zip", "wb").write(zip_bytes)

Project Structure

EditR_wang/
├── R/                        # R 模块
│   ├── analysis.R           # 核心分析(getEditing、guide 匹配、ZAGA 噪声模型)
│   ├── data_processing.R    # 数据处理、重命名映射、图表、ZIP 打包、共享分析入口
│   └── database.R           # SQLite 操作(模板 / 正则预设)
├── api.R                    # plumber API 定义(全部端点)
├── start_api.R              # API 启动脚本
├── dependencies.R           # R 依赖安装
├── frontend/                # Next.js 前端
│   └── src/
│       ├── app/page.tsx     # 主页面(上传/分析/下载流程编排)
│       ├── components/      # FileUploader / RenamePanel / Charts 等
│       └── lib/api.ts       # 后端 API 调用封装
├── EditRBatch/              # 独立批量分析系统
└── deploy/                  # 部署配置

Development

新增一个 API 端点:

  1. api.R 用 plumber 注解定义端点(#* @post /api/...)。
  2. frontend/src/lib/api.ts 添加对应调用函数。
  3. 在前端组件中调用。

License

GPL License

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors