tbtools-cli 1.2.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.
- tbtools_cli/__init__.py +18 -0
- tbtools_cli/auto_commands.py +1149 -0
- tbtools_cli/cli.py +528 -0
- tbtools_cli/cli_load.py +352 -0
- tbtools_cli/cli_rpc.py +303 -0
- tbtools_cli/cli_tools_registry.py +93 -0
- tbtools_cli/cli_top.py +1160 -0
- tbtools_cli/command_metadata.json +4606 -0
- tbtools_cli/command_spec.py +382 -0
- tbtools_cli/config.example.toml +19 -0
- tbtools_cli/config.py +43 -0
- tbtools_cli/core.py +508 -0
- tbtools_cli/errors.py +38 -0
- tbtools_cli/presets.py +100 -0
- tbtools_cli/scenarios.py +92 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/AdmixtureCli.java +91 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/AmazingMetaCli.java +78 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/AssemblyRecommandCli.java +47 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BamIndexCli.java +31 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BamSortCli.java +43 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BamStateCli.java +49 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BarPlotterCli.java +18 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BarplotCli.java +168 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BatchVizMotifsCli.java +47 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BlastXmlConvertCli.java +45 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/BlatExecutorCli.java +66 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CalcRepeatCli.java +62 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CalculateSimilarityCli.java +34 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CddMotifCli.java +46 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CircleGeneViewerCli.java +81 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CircosCli.java +76 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/ColorSchemeCli.java +29 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CtgGroupCli.java +29 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/CubeHeatmapCli.java +70 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DeHistCli.java +46 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DegramdomCli.java +33 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DiffExpCli.java +44 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DistanceCli.java +53 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/DualSynCli.java +147 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/EggnogCli.java +39 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/ExprCorrCli.java +27 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FastaMergerCli.java +36 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FastaTableConvertCli.java +38 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FileCleanerCli.java +31 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FileSplitCli.java +15 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FindBlockDualCli.java +71 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FindBlockMultipleCli.java +67 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/FindPathCli.java +15 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GSEAWrapperCli.java +33 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GenBank2FastaCli.java +32 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GeneDensityCli.java +38 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GeneLocGffCli.java +140 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GeneStructureCli.java +90 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GenericCli.java +177 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GetENALinksCli.java +36 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GetSubNewickTreeCli.java +50 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GoEnrichCli.java +42 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GoLevelCli.java +59 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GroupedBarCli.java +95 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GsaDiagCli.java +45 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GxfFilterCli.java +39 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/GxfSortCli.java +23 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/HclustCli.java +43 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/HeatmapCli.java +89 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/HmmerSuiteCli.java +47 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/JgblocksCli.java +65 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/KeggEnrichCli.java +31 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/LayoutHeatmapCli.java +90 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MCScanXCli.java +80 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MCScanXFastCli.java +41 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MSACli.java +46 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MakeMotifCli.java +39 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MarkerDesignCli.java +62 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MarkerToolsCli.java +57 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Mast2TabCli.java +27 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MastCli.java +54 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MastRunCli.java +39 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Meme2TabCli.java +26 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MemeCli.java +74 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MemeRunCli.java +40 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MgGxfCli.java +35 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MicroSynCli.java +118 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MirIdentifyCli.java +55 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MotifCli.java +75 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MotifPatternCli.java +67 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MotifShiftCli.java +30 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MountainPlotCli.java +44 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/MultiSuperHeatCli.java +64 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/NeedlemanWunschCli.java +76 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PafGC.java +15 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PafVizCli.java +67 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PeakDistCli.java +58 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Pep2CodonCli.java +26 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PfamMotifCli.java +43 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PhyloTreeCli.java +92 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PileUpCli.java +55 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PlantCAREResultClassifyCli.java +36 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/PubmedSearchCli.java +25 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QpcrCli.java +69 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QpcrDdctCli.java +28 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QpcrProcCli.java +28 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QuickGenomeDotCli.java +66 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QuickProteinAnnoCli.java +34 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QuickRunIQtreeCli.java +69 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/QuickTrimALCli.java +67 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/RNAplotCli.java +129 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/RegionBedToGFF3Cli.java +43 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/RegionDepthCli.java +29 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SamBamCovCli.java +33 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SeqConverterCli.java +17 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SeqLenTrackCli.java +41 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SeveralSpeciesCli.java +132 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SimpleEnricherCli.java +44 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SimpleHmmscanCli.java +32 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SixFrameTranlaterCli.java +35 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/StructAnnoCompareCli.java +66 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SubmitSMARTCli.java +30 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/SuperCircosCli.java +330 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TableColManipCli.java +56 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TableCollapseCli.java +33 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TargetScoreCli.java +47 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TauCalcCli.java +28 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TaxonomyBatchCli.java +72 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TreeCli.java +75 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TreeRootingCli.java +30 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/TrimMSACli.java +33 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/UnrootedTreeCli.java +44 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/UpSetCli.java +90 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Venn5Cli.java +60 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/Venn6Cli.java +65 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/ViolinCli.java +66 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/VisualizeCli.java +57 -0
- tbtools_cli-1.2.0.data/data/tbtools_cli/bridges/VizGFACli.java +39 -0
- tbtools_cli-1.2.0.dist-info/METADATA +504 -0
- tbtools_cli-1.2.0.dist-info/RECORD +139 -0
- tbtools_cli-1.2.0.dist-info/WHEEL +5 -0
- tbtools_cli-1.2.0.dist-info/entry_points.txt +2 -0
- tbtools_cli-1.2.0.dist-info/licenses/LICENSE +21 -0
- tbtools_cli-1.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1149 @@
|
|
|
1
|
+
"""auto_commands:命令实现层(表驱动化 v2)。
|
|
2
|
+
|
|
3
|
+
144 个同构命令(86 bridge + 58 direct Java 调用)由 ENGINE_REGISTRY 数据驱动生成,
|
|
4
|
+
13 个特殊实现手写保留(hmmsearch 转发 / gxfAttr 原生 / kallisto 二进制 / fimo 二进制 /
|
|
5
|
+
notung 插件 / newickRename 插件 / hmmerSearch / memeViz / gsea / tfbsShift /
|
|
6
|
+
mcscanxd / quickAnno / smart —— 各含独立预检/环境/参考数据逻辑)。
|
|
7
|
+
|
|
8
|
+
cli.py 通过 dir(_ac) 反射 _xxx_impl 名字注册命令,函数形态必须保留。
|
|
9
|
+
doc 值为旧模块运行时 __doc__(已含编译器 docstring 处理后的真实字符)。
|
|
10
|
+
"""
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
import shutil
|
|
14
|
+
import subprocess
|
|
15
|
+
import sys
|
|
16
|
+
import tempfile
|
|
17
|
+
|
|
18
|
+
from tbtools_cli.core import BUILD_DIR, JAR, ROOT, cp, ensure_bridge, run_java, run_plot, safe_temp
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ── ENGINE_REGISTRY ──────────────────────────────────────────────
|
|
22
|
+
# (命令名, kind, 类名, xmx, runner, doc)
|
|
23
|
+
# kind: bridge = 本地桥(ensure_bridge + cp(BUILD_DIR,JAR))
|
|
24
|
+
# direct = 直连主 jar 引擎类(cp JAR + biocjava.* 全限定名)
|
|
25
|
+
# runner: plot = run_plot(xvfb 包装)/ java = run_java
|
|
26
|
+
ENGINE_REGISTRY = [
|
|
27
|
+
('admixture', 'bridge', 'AdmixtureCli', '3g', 'plot', 'admixture: admixture <qFiles.lst> <out> [sampleIDFile] [groupFile] [sor'),
|
|
28
|
+
('admixtureViz', 'bridge', 'AdmixtureCli', '3g', 'plot', 'admixtureViz: admixtureViz <q1.txt> <q2.txt> [<q3.txt>...] <out.svg> [--id samples.txt] [--group group.txt] [--sort Qraito|Lexical|None] # ADMIXTURE Q 矩阵可视化(GUI 逆向接口;Q 文件纯数值矩阵,样本 ID 单独 --id)'),
|
|
29
|
+
('amazingmeta', 'bridge', 'AmazingMetaCli', '3g', 'plot', 'amazingmeta: amazingmeta <meme.xml> <newick.treefile> <out.svg|png|pdf> ['),
|
|
30
|
+
('annocompare', 'bridge', 'StructAnnoCompareCli', '3g', 'plot', 'annocompare: annocompare <before.gff3> <after.gff3> <outDir> [runName] [r'),
|
|
31
|
+
('bamMerge', 'direct', 'biocjava.bioDoer.GenomeAnnotation.BAMMergeByRegionCoverage', '3g', 'plot', 'bamMerge: bamMerge <gtf> <bamDir> <outDir> # 按区域覆盖合并 BAM(多样本择优)'),
|
|
32
|
+
('bamindex', 'bridge', 'BamIndexCli', '3g', 'plot', 'bamindex: bamindex <in.sorted.bam> [out.bai]'),
|
|
33
|
+
('bamsort', 'bridge', 'BamSortCli', '3g', 'plot', 'bamsort: bamsort <in.bam> <out.bam> [sortOrder] [tmpDir]'),
|
|
34
|
+
('bamstate', 'bridge', 'BamStateCli', '3g', 'plot', 'bamstate: bamstate <out.tsv> <gff3> <bam1> [<bam2> ...]'),
|
|
35
|
+
('barplot', 'bridge', 'BarplotCli', '3g', 'plot', 'barplot: barplot <enrichment.tsv> <out> <termCol> <pvalCol> [classCol'),
|
|
36
|
+
('barplotter', 'bridge', 'BarPlotterCli', '3g', 'plot', 'barplotter: barplotter -g <gff> -s <synteny> -c <ctl> -o <out.png>'),
|
|
37
|
+
('batchReplace', 'direct', 'biocjava.bioDoer.Table.BatchStringReplace', '3g', 'plot', 'batchReplace: batchReplace <inFile> <outFile> <patternMap.tsv> [--partial]'),
|
|
38
|
+
('calcRepeat', 'bridge', 'CalcRepeatCli', '3g', 'plot', 'calcRepeat: calcRepeat <genome.fa> <outRepeat.txt> [--kmerSize N] [--min'),
|
|
39
|
+
('cddmotif', 'bridge', 'CddMotifCli', '3g', 'plot', 'cddmotif: cddmotif <cdd.hitdata.txt> <in.fasta> <out.svg|png|pdf> [new'),
|
|
40
|
+
('circlegene', 'bridge', 'CircleGeneViewerCli', '3g', 'plot', 'circlegene: circlegene <gff> <geneID.txt> <out> [--rename f --link f --r'),
|
|
41
|
+
('circos', 'bridge', 'CircosCli', '3g', 'plot', 'circos: circos <chrLen.txt> <link.txt> <genePos.txt> <outFile> [w] ['),
|
|
42
|
+
('collinearRegion', 'direct', 'biocjava.bioDoer.ComparativeGenomics.MCScanX.CollinearityToRegion', '3g', 'plot', 'collinearRegion: collinearRegion <in.collinearity> <simGff> <out.txt>'),
|
|
43
|
+
('colorscheme', 'bridge', 'ColorSchemeCli', '3g', 'plot', 'colorscheme: colorscheme <inTab> <outTab> <refColIndex>'),
|
|
44
|
+
('conflictpaf', 'direct', 'biocjava.bioDoer.GenomeAssembly.CalculateConflictByRefAlignPAF', '3g', 'plot', 'conflictpaf: conflictpaf <in.paf> <out.tsv> [binSize]'),
|
|
45
|
+
('ctgGroup', 'bridge', 'CtgGroupCli', '3g', 'plot', 'ctgGroup: ctgGroup <in.miniprot.gff> <polyPoid> <outContigGrpMap>'),
|
|
46
|
+
('cubeheatmap', 'bridge', 'CubeHeatmapCli', '3g', 'plot', 'cubeheatmap: cubeheatmap <expr.tsv> <group.tsv> <out> [--log10 --minColor <c> --midColor <c> --maxColor <c>] # 3D 立方体热图(N4: group.tsv 引擎对列数有严格假设,官方 cube_group.tsv 仍会 ArrayIndexOutOfBounds——引擎缺陷;喂分组格式: 行=样本/基因,列数须与引擎预期一致,建议先 tbtools check)'),
|
|
47
|
+
('degramdom', 'bridge', 'DegramdomCli', '3g', 'plot', 'degramdom: degramdom <in.tsv> [out.nwk]'),
|
|
48
|
+
('distance', 'bridge', 'DistanceCli', '3g', 'plot', 'distance: distance <in.tsv> <col1> <col2> <euclidean|pearson|pearsonDi'),
|
|
49
|
+
('dotplot', 'direct', 'biocjava.bioDoer.JIGplotToolkit.DotPlot.dotdotdot', '3g', 'plot', 'dotplot: dotplot --inGff <gff> --genePair <pairs> --chrLayout <layout'),
|
|
50
|
+
('dualsyn', 'bridge', 'DualSynCli', '3g', 'plot', 'dualsyn: dualsyn <simplifiedGff> <collinearity> <out> [--chr1 "1,2"] '),
|
|
51
|
+
('efpHeat', 'direct', 'biocjava.bioDoer.SimpleEfpBrowser.generateSuperHeatMap', '3g', 'plot', 'efpHeat: efpHeat <inTGA> <sample2cc.txt> <expMat.tsv> <geneId> <out.s'),
|
|
52
|
+
('eggnog', 'bridge', 'EggnogCli', '4g', 'java', 'eggnog: eggnog <in.fa> -o <prefix> --output_dir <outDir> --data_dir <eggNOGdb> [--cpu N] [--evalue 0.001] # eggNOG 直系同源注释(GUI 逆向接口 EmapperPipeline;⚠️ 需先就位 eggNOG 数据库)'),
|
|
53
|
+
('exprCorr', 'bridge', 'ExprCorrCli', '3g', 'plot', 'exprCorr: exprCorr <inFPKM> <outCorrMat>'),
|
|
54
|
+
('fastaExtract', 'direct', 'biocjava.bioDoer.Fasta.ExtractFasta', '3g', 'plot', 'fastaExtract: fastaExtract <in.fa> <idList.txt> <out.fa> [--mode Match|Con'),
|
|
55
|
+
('fastaSubseq', 'direct', 'biocjava.bioDoer.Fasta.ExtractFastaSubseq', '3g', 'plot', 'fastaSubseq: fastaSubseq <in.fa> <pos.txt> <out.fa> # 按坐标提子序列(第92引擎,Ext'),
|
|
56
|
+
('filesplit', 'bridge', 'FileSplitCli', '3g', 'plot', 'filesplit: filesplit <inFile> <numParts>'),
|
|
57
|
+
('filterCScore', 'direct', 'biocjava.bioDoer.BLAST.FilterBlastResultByCScore', '3g', 'plot', 'filterCScore: filterCScore <in.blast.tab6> <out.tab6> [--cscore 0.5]'),
|
|
58
|
+
('findblockdual', 'bridge', 'FindBlockDualCli', '3g', 'plot', 'findblockdual: findblockdual <queryGenome.fa> <query.gff> <subjectGenome.fa'),
|
|
59
|
+
('findblockmultiple', 'bridge', 'FindBlockMultipleCli', '3g', 'plot', 'findblockmultiple: findblockmultiple <queryGenome.fa> <query.gff> <queryId> <ou'),
|
|
60
|
+
('findpath', 'bridge', 'FindPathCli', '3g', 'plot', 'findpath: findpath --inGffArr <gff1,gff2,...> --inGenePairs <pairs> --'),
|
|
61
|
+
('fqTrim', 'direct', 'biocjava.bioDoer.Fastq.FastqParallelTrimmer', '3g', 'plot', 'fqTrim: fqTrim <in.fq> <out.fq> [--b5 N] [--b3 N] [--threads N]'),
|
|
62
|
+
('fqfaConv', 'direct', 'biocjava.bioDoer.LinuxPipe.FastqAndFasta', '3g', 'plot', 'fqfaConv: fqfaConv <input> <output> <fq2fa|fa2fq> # FASTQ/FASTA 互转(第'),
|
|
63
|
+
('gbar', 'bridge', 'GroupedBarCli', '3g', 'plot', 'gbar: gbar <data.tsv> <out.svg> [--header|--no-header] [--errorbar SEM|SD|CI95] [--plot BAR_ERROR|BOXPLOT|VIOLIN|SWARM] [--homoscedastic-t] # 分组柱状图+显著性标注(GUI 逆向接口 buildPanel;数据=每行 group value)'),
|
|
64
|
+
('gdensity', 'bridge', 'GeneDensityCli', '3g', 'java', 'gdensity: gdensity <in.gff3> <out.geneRecords> <binSize> [--feature <tag>] [--chrlen <file>] # 基因密度 bin 分析(GUI 逆向接口 GeneDensityProfiler)'),
|
|
65
|
+
('gel', 'direct', 'biocjava.bioDoer.JIGplotToolkit.GelImage.Marker', '3g', 'plot', 'gel: gel <FragmentRangeArr> <LaneLabels> <MarkerRange> <out> # 凝胶电泳图(GelImage.Marker;⚠️ 无参调用会挂起 N32)'),
|
|
66
|
+
('genedensity', 'bridge', 'GeneDensityCli', '3g', 'plot', 'genedensity: genedensity <in.gff3> <out.tsv> [binSize]'),
|
|
67
|
+
('genelocation', 'direct', 'biocjava.bioDoer.JIGplotToolkit.GeneLocation.GeneLocation', '3g', 'plot', 'genelocation: genelocation --ChrLen <chrlen> --FeaturePos <pos> --OutGraph'),
|
|
68
|
+
('genelocgff', 'bridge', 'GeneLocGffCli', '3g', 'plot', 'genelocgff: genelocgff <gff3> <idList> <out> [--chrLen len.tsv] [--renam'),
|
|
69
|
+
('generic', 'bridge', 'GenericCli', '3g', 'plot', 'generic: generic <engineClass> <method[+method2]> <out> [--set field '),
|
|
70
|
+
('gfa', 'bridge', 'VizGFACli', '3g', 'plot', 'gfa: gfa <in.gfa> <out> [width] [height]'),
|
|
71
|
+
('gfa2fa', 'direct', 'biocjava.bioDoer.Fasta.Tools.GFAtoFasta', '3g', 'plot', 'gfa2fa: gfa2fa <in.gfa> <out.fa> # GFA 组装图 → FASTA(第91引擎,GFAtoFast'),
|
|
72
|
+
('goEnrich', 'bridge', 'GoEnrichCli', '4g', 'java', 'goEnrich: goEnrich <go.obo> <gene2go.tsv> <selectGenes.txt> <outDir> # GO 富集分析(MF/CC/BP,P+BH 校正,G4 补齐)'),
|
|
73
|
+
('goParse', 'direct', 'biocjava.bioDoer.GeneOntology.littleTools.GOtermParser', '3g', 'plot', 'goParse: goParse <gene2Go.txt> <oboFile> [--level N] # GO 词典解析(第103'),
|
|
74
|
+
('golevel', 'bridge', 'GoLevelCli', '3g', 'plot', 'golevel: golevel <go.obo> <gene2go.tsv> <outPrefix> [--level N] [--graph] [--width W] [--height H] # GO 层级统计+柱状图(GUI 逆向接口;统计表纯逻辑,图需 xvfb)'),
|
|
75
|
+
('groupCol', 'direct', 'biocjava.bioDoer.Table.TableColCollaspe', '3g', 'plot', 'groupCol: groupCol <inTable.tsv> <inGrpInfo.tsv> <outTable> [Sum|Mean|'),
|
|
76
|
+
('groupedbar', 'bridge', 'GroupedBarCli', '3g', 'plot', 'groupedbar: groupedbar <data.tsv> <out> [plotType] [errorBarType] [hasHeader] [title] # 分组柱状图(N5: 数据格式=每行 <group>\t<value>(重复行成组),非常规基因×样本矩阵;矩阵输入会在 GroupedBarRawData.load 崩溃——引擎缺陷)'),
|
|
77
|
+
('gsadiag', 'bridge', 'GsaDiagCli', '3g', 'plot', 'gsadiag: gsadiag <in.fixed.gff3> <out.stat.xls> [genome.fasta] [relax'),
|
|
78
|
+
('gxfAppend', 'direct', 'biocjava.bioDoer.GXFUtils.GxfIDAppender', '3g', 'plot', 'gxfAppend: gxfAppend <in.gff3> <out.gff3> <prefix> # GFF seqid+ID 加前缀'),
|
|
79
|
+
('gxfFix', 'direct', 'biocjava.bioDoer.GXFUtils.GXFfixer.GXFFix', '3g', 'plot', 'gxfFix: gxfFix <in.gff3> <out.gff3> # GFF 修复(重复ID前缀/CDS phase/dang'),
|
|
80
|
+
('gxfGenepos', 'direct', 'biocjava.bioDoer.GXFUtils.GXFToGenePosFile', '3g', 'plot', 'gxfGenepos: gxfGenepos <in.gff3> <outGenepos> <outChrLen> [feature] # G'),
|
|
81
|
+
('gxfMatch', 'direct', 'biocjava.bioDoer.GXFUtils.GxfGenomeMatch', '3g', 'plot', 'gxfMatch: gxfMatch <in.gff3> <inGenome.fa>'),
|
|
82
|
+
('gxfOverlap', 'direct', 'biocjava.bioDoer.GXFUtils.GXFOverlaper', '3g', 'plot', 'gxfOverlap: gxfOverlap <in.gff3> <region.txt> <out.gff3> [--ignoreStrand'),
|
|
83
|
+
('gxfRecall', 'direct', 'biocjava.bioDoer.GXFUtils.RecallmRNAFeature', '3g', 'plot', 'gxfRecall: gxfRecall <in.gff3> <out.gff3> # 从 gene 行恢复 mRNA 特征(第82引擎,'),
|
|
84
|
+
('gxfRegion', 'direct', 'biocjava.bioDoer.GXFUtils.GXFRegionSummary', '3g', 'plot', 'gxfRegion: gxfRegion <in.gff3> <region.txt> <out.gff3> [--ignoreStrand]'),
|
|
85
|
+
('gxfRename', 'direct', 'biocjava.bioDoer.GXFUtils.GXFRenamer', '3g', 'plot', 'gxfRename: gxfRename <in.gff3> <out.gff3> <renameMap.tsv>'),
|
|
86
|
+
('gxfRepGXF', 'direct', 'biocjava.bioDoer.GXFUtils.GXFToRepresentativeGXF', '3g', 'plot', 'gxfRepGXF: gxfRepGXF <in.gff3> <out.gff3> [--featureID CDS] [--attachID'),
|
|
87
|
+
('gxfRepIDs', 'direct', 'biocjava.bioDoer.GXFUtils.GXFToRepresentativeIDs', '3g', 'plot', 'gxfRepIDs: gxfRepIDs <in.gff3> <out.txt>'),
|
|
88
|
+
('gxfStat', 'direct', 'biocjava.bioDoer.GXFUtils.GXFfixer.GXFstat', '3g', 'plot', 'gxfStat: gxfStat <in.gff3> <outStat.xls> # GFF 统计(基因/mRNA/外显子/内含子/C'),
|
|
89
|
+
('gxffilter', 'bridge', 'GxfFilterCli', '3g', 'plot', 'gxffilter: gxffilter <in.gff3|gtf> <idList.txt> <out.gff3|gtf>'),
|
|
90
|
+
('gxfsort', 'bridge', 'GxfSortCli', '3g', 'plot', 'gxfsort: gxfsort <in.gff3|gtf> <out.sorted>'),
|
|
91
|
+
('hicEnzyme', 'direct', 'biocjava.bioDoer.GenomeAssembly.HiCRestrictionEnzymePrediction', '3g', 'plot', 'hicEnzyme: hicEnzyme <inHiC.fastq> # HiC 限制酶预测(第76引擎)'),
|
|
92
|
+
('hmmExtract', 'direct', 'biocjava.bioDoer.LinuxPipe.hmmInfoExtracter', '3g', 'plot', 'hmmExtract: hmmExtract <in.hmm> <idList.txt> <out.hmm> # 从 HMM 文件按 NAM'),
|
|
93
|
+
('homoPhase', 'direct', 'biocjava.bioDoer.GenomeAssembly.HomoConflictBasedPartition', '3g', 'plot', 'homoPhase: homoPhase <inContigGrpMap> <outPhasedMap>'),
|
|
94
|
+
('keggEnrich', 'bridge', 'KeggEnrichCli', '4g', 'java', 'keggEnrich: keggEnrich <reference.keg> <annotation.tsv> <selectIds.txt> <out.xls> # KEGG 富集分析(G4 补齐,需真实 .keg 参考文件)'),
|
|
95
|
+
('layoutheatmap', 'bridge', 'LayoutHeatmapCli', '3g', 'plot', 'layoutheatmap: layoutheatmap <layout.tsv> <expr.tsv> <out> [--options]'),
|
|
96
|
+
('levelGo', 'direct', 'biocjava.bioDoer.GeneOntology.Grapher.LevelDoer', '3g', 'plot', 'levelGo: levelGo <gene2Go.txt> <outTable> <oboFile> [--level N]'),
|
|
97
|
+
('makemotif', 'bridge', 'MakeMotifCli', '3g', 'java', 'makemotif: makemotif <in.seqs.txt> <out.meme> [--mol DNA|RNA|Protein] # 等长序列→MEME motif 文件(GUI 逆向接口;产物可直接喂 fimo/mast)'),
|
|
98
|
+
('marker', 'direct', 'biocjava.bioDoer.markerDesign.BigMarkerRandomDesign', '3g', 'plot', 'marker: marker <MarkerDist|MarkerFilter|SampleDist|BigMarkerRandomDe'),
|
|
99
|
+
('markertools', 'bridge', 'MarkerToolsCli', '3g', 'plot', 'markertools: markertools <filter|dist|sampledist> <in.marker.tab> [maxPoi'),
|
|
100
|
+
('mast', 'bridge', 'MastCli', '3g', 'java', 'mast: mast <sequence.fa> <motifs.meme|meme.xml> <workingDir> [--motif-to-use N] [--max-motif-pvalue 0.0001] [--max-seq-evalue 10] # MAST motif 搜索(GUI 逆向接口 QuickRunMAST,需系统 mast;产物 mast.html/txt/xml)'),
|
|
101
|
+
('mast2tab', 'bridge', 'Mast2TabCli', '3g', 'plot', 'mast2tab: mast2tab <mast|meme.xml> <out.tab>'),
|
|
102
|
+
('mastExtract', 'direct', 'biocjava.bioDoer.MEME.ExtractSeq.ExtractSeqFromMastXML', '3g', 'plot', 'mastExtract: mastExtract <in.fa> <mast.xml> <out.txt> # 从 MAST XML 提取命中'),
|
|
103
|
+
('mastrun', 'bridge', 'MastRunCli', '3g', 'plot', 'mastrun: mastrun <meme.xml> <seq.fasta> <workingDir> [--motifs M] [--'),
|
|
104
|
+
('mcscanx', 'bridge', 'MCScanXCli', '3g', 'plot', 'mcscanx: mcscanx <gff> <blast> <outPrefix> [--html] # 共线性检测'),
|
|
105
|
+
('meme', 'bridge', 'MemeCli', '3g', 'java', 'meme: meme <in.fa> <workingDir> <outMemeXml> [--nmotifs N] [--minw N] [--maxw N] [--evt 0.05] [--mod zoops|oops|anr] # MEME motif 发现(GUI 逆向接口 QuickRunMEME,需系统 meme;产物可与 memeViz/fimo 串联)'),
|
|
106
|
+
('meme2tab', 'bridge', 'Meme2TabCli', '3g', 'java', 'meme2tab: meme2tab <meme.xml|mast.xml> <out.tab> # MEME/MAST XML→motif 域表(GUI 逆向接口 MEMESuiteXMLtoTab)'),
|
|
107
|
+
('memerun', 'bridge', 'MemeRunCli', '3g', 'plot', 'memerun: memerun <in.fasta> <workingDir> [--motif N] [--minW N] [--ma'),
|
|
108
|
+
('mggxf', 'bridge', 'MgGxfCli', '3g', 'plot', 'mggxf: mggxf <inGenePair|blastTab6> <in.simplified.gff> <out.Linked'),
|
|
109
|
+
('microgenome', 'direct', 'biocjava.bioDoer.JIGplotToolkit.MicroGenomeViz.MicroGenomeAnnotationCircosPlot', '3g', 'plot', 'microgenome: microgenome <inGBK> <anno.tsv> <out> [micro|macro]'),
|
|
110
|
+
('microsyn', 'bridge', 'MicroSynCli', '3g', 'plot', 'microsyn: microsyn <gxf1> <gxf2> <collinearity> <out> [--chr1 C --star'),
|
|
111
|
+
('mirnaIdentify', 'bridge', 'MirIdentifyCli', '3g', 'plot', 'mirnaIdentify: mirnaIdentify <genome.fa> <targetSo.tsv> <outPredict.txt> <outChecklog.txt> [--checkARM BOTH|FIVE|THREE] [--maxAsy N] [--maxBulge N] # miRNA 前体鉴定(GUI 逆向 #78 MirIdentifyCli;⚠️ 第 4 参 outChecklog 必需,docstring 原漏写 N29)'),
|
|
112
|
+
('mirnaTarget2', 'direct', 'biocjava.bioDoer.miRNA.Target2TablePipe', '3g', 'plot', 'mirnaTarget2: mirnaTarget2 <mirna.fa> <target.fa> <out.txt> [--revCom true'),
|
|
113
|
+
('mountain', 'bridge', 'MountainPlotCli', '3g', 'plot', 'mountain: mountain <fold.txt> <out.tsv>'),
|
|
114
|
+
('mpattern', 'bridge', 'MotifPatternCli', '3g', 'plot', 'mpattern: mpattern <mast.xml> <out.svg> [--max-motif N] [--shape RoundRect|Rect|Oval] [--line Middle|Up|Down|Splice] [--gradient] [--show-num] # MEME/MAST motif 序列标注图(GUI 逆向接口,postGraph(String,panel) 重载绕弹窗)'),
|
|
115
|
+
('multiEfp', 'bridge', 'MultiSuperHeatCli', '3g', 'plot', 'multiEfp: multiEfp <inTGA> <sample2cc> <expMat1[,expMat2,...]> <geneId'),
|
|
116
|
+
('multisyn', 'bridge', 'SeveralSpeciesCli', '3g', 'plot', 'multisyn: multisyn <gxf.lst> <collinear.lst> <out> [--genes idlist.txt'),
|
|
117
|
+
('nwAlign', 'bridge', 'NeedlemanWunschCli', '3g', 'plot', 'nwAlign: nwAlign <seq1.fa> <seq2.fa> <out> [--protein|--dna] [--format EMBOSS|FASTA] [--gap-open N] [--gap-extend N] [--end-gap-open N] [--end-gap-extend N] [--end-weight] # Needleman-Wunsch 全局比对(GUI 逆向接口 NeedleManWunschAlign;旧 SimpleBatchProcess 静默无产物已替换)'),
|
|
118
|
+
('pafcomp', 'bridge', 'PafGC', '3g', 'plot', 'pafcomp: pafcomp --inPaf <paf> --outGraph <out> [--colorMode Target|Q'),
|
|
119
|
+
('pafref', 'direct', 'biocjava.bioDoer.JIGplotToolkit.Paf.PafRefBaseCoverCalc', '3g', 'plot', 'pafref: pafref --inPaf <paf> --outTab <out.tsv>'),
|
|
120
|
+
('pafviz', 'bridge', 'PafVizCli', '3g', 'plot', 'pafviz: pafviz <in.paf> <out.svg> [--graph-size N] [--color Target|Query|None] [--seed N] [--min-len N] [--switch-qnt] [--rc-color] # PAF 比对 dot 图(GUI 逆向接口 PafViz.process,绕 quickShow)'),
|
|
121
|
+
('partitionconflict', 'direct', 'biocjava.bioDoer.GenomeAssembly.ParititionByConflictFreq', '3g', 'plot', 'partitionconflict: partitionconflict <inConflictFreq.tsv> <polyPoid> <outCluste'),
|
|
122
|
+
('peakanno', 'direct', 'biocjava.bioDoer.JIGplotToolkit.MACS2viz.peakAnno', '3g', 'plot', 'peakanno: peakanno <gxf> <macs2_peak.xls> <out.tsv> [--dist N]'),
|
|
123
|
+
('peakdist', 'bridge', 'PeakDistCli', '3g', 'plot', 'peakdist: peakdist <chrLen.tsv> <macs2_peak.xls> <out> [--chrHeight H]'),
|
|
124
|
+
('peaktss', 'direct', 'biocjava.bioDoer.JIGplotToolkit.MACS2viz.peakTssHeatMap', '3g', 'plot', 'peaktss: peaktss <gxf> <macs2_peak.xls> <out.svg/png> [--dist N] [--b'),
|
|
125
|
+
('pep2codon', 'bridge', 'Pep2CodonCli', '3g', 'plot', 'pep2codon: pep2codon <cds.fa> <pep.aln.fa> <codon.aln.out>'),
|
|
126
|
+
('pfammotif', 'bridge', 'PfamMotifCli', '3g', 'plot', 'pfammotif: pfammotif <pfamscan.txt> <in.fasta> <out.svg|png|pdf> [newic'),
|
|
127
|
+
('phylotree', 'bridge', 'PhyloTreeCli', '3g', 'plot', 'phylotree: phylotree <in.nwk> <out> [vertical] [width] [height]'),
|
|
128
|
+
('pileup', 'bridge', 'PileUpCli', '3g', 'plot', 'pileup: pileup <blast.xml> <out.svg> [--query NAME]'),
|
|
129
|
+
('plotrna', 'direct', 'biocjava.bioDoer.JIGplotToolkit.miRCoverage.PlotRNAfold', '3g', 'plot', 'plotrna: plotrna <genomeFA> <region> <SAM> [--directPDF out.pdf]'),
|
|
130
|
+
('preparespecies', 'direct', 'biocjava.bioDoer.ComparativeGenomics.PrepareSpecies', '3g', 'plot', 'preparespecies: preparespecies <prefix> <inGenome.fa> <inGFF> <outGenome.fa>'),
|
|
131
|
+
('qdot', 'bridge', 'QuickGenomeDotCli', '3g', 'plot', 'qdot: qdot <blast.tab> <in.gff> <chrLayout.txt> <out.svg> [--point-size N] [--highlight genes.txt] # 基因组 dot plot(插件 P00380 CLI 化;blast/gff/chrLayout 可由 mcscanxd 产出,绕开插件 quickShow GUI 崩溃直驱 dotdotdot)'),
|
|
132
|
+
('qpcr', 'bridge', 'QpcrCli', '3g', 'plot', 'qpcr: qpcr <data.txt> <out> [w] [h] (data: name mean sd)'),
|
|
133
|
+
('qpcrExp', 'bridge', 'QpcrDdctCli', '3g', 'plot', 'qpcrExp: qpcrExp <in.qpcr.tab> <out.xls>'),
|
|
134
|
+
('qpcrproc', 'bridge', 'QpcrProcCli', '3g', 'plot', 'qpcrproc: qpcrproc <in.qpcr.tab> <out.xls>'),
|
|
135
|
+
('quickFamily', 'direct', 'biocjava.bioDoer.BLAST.ReciprocalBlast.QuickGeneFamilyIdentification', '3g', 'plot', 'quickFamily: quickFamily <refPep.fa> <familyIds.txt> <queryPep.fa> <outPr'),
|
|
136
|
+
('recipBlast', 'direct', 'biocjava.bioDoer.BLAST.ReciprocalBlast.ReciprocalBlast', '3g', 'plot', 'recipBlast: recipBlast <query.fa> <subject.fa> <outPrefix> [--queryIds i'),
|
|
137
|
+
('regionAnno', 'direct', 'biocjava.bioDoer.GXFUtils.RegionGXFOverlapAnnotation', '3g', 'plot', 'regionAnno: regionAnno <in.gff3> <region.txt> <outTab> [--flankLen N] [-'),
|
|
138
|
+
('regiondepth', 'bridge', 'RegionDepthCli', '3g', 'plot', 'regiondepth: regiondepth <in.sam> <region> <out.depth> [scaleFactor]'),
|
|
139
|
+
('rnaplot', 'bridge', 'RNAplotCli', '3g', 'plot', 'rnaplot: rnaplot <seq.fa|rawSeq> <out> [--colorMap "seq1=R,G,B;seq2=R'),
|
|
140
|
+
('sambamcov', 'bridge', 'SamBamCovCli', '3g', 'plot', 'sambamcov: sambamcov <in.bam> <out.tsv> [binSize] [countMode]'),
|
|
141
|
+
('sepChr', 'direct', 'biocjava.bioDoer.GenomeAssembly.SeperateChrByAlleles', '3g', 'plot', 'sepChr: sepChr <gene2chr.tsv> <in.miniprot.gff> <outMap>'),
|
|
142
|
+
('seqconvert', 'bridge', 'SeqConverterCli', '3g', 'plot', 'seqconvert: seqconvert -i <in> -o <out> -iF <fmt> -oF <fmt>'),
|
|
143
|
+
('seqlentrack', 'bridge', 'SeqLenTrackCli', '3g', 'plot', 'seqlentrack: seqlentrack <seqlen.txt> <out.svg|png|pdf> [newick.treefile]'),
|
|
144
|
+
('simplehmmscan', 'bridge', 'SimpleHmmscanCli', '3g', 'plot', 'simplehmmscan: simplehmmscan <pfamA.hmm> <target.pep> <idList.txt> <out.txt'),
|
|
145
|
+
('sricher', 'bridge', 'SimpleEnricherCli', '3g', 'java', 'sricher: sricher <in.tsv> <out.xls> <totalAnnoIdx> <totalHitIdx> <selAnnoIdx> <selHitIdx> [--header] # 简单富集(GUI 逆向接口 SimpleEnricher,超几何+BH;goEnrich 轻量版无需 OBO)'),
|
|
146
|
+
('supercircos', 'bridge', 'SuperCircosCli', '3g', 'plot', 'supercircos: supercircos <config.cfg> <out> [width] [height]'),
|
|
147
|
+
('tableAppend', 'direct', 'biocjava.bioDoer.Table.TableAppend', '3g', 'plot', 'tableAppend: tableAppend <inTab1> <inTab2> <outTab> [--c1 N] [--c2 N] #'),
|
|
148
|
+
('tableCast', 'direct', 'biocjava.bioDoer.Table.TableCast', '3g', 'plot', 'tableCast: tableCast <inLong.txt> <outMatrix>'),
|
|
149
|
+
('tableColSel', 'direct', 'biocjava.bioDoer.Table.TableColSelector', '3g', 'plot', 'tableColSel: tableColSel <inTable> <outTable> <idList.txt> [--mode Match|'),
|
|
150
|
+
('tableColSelect', 'bridge', 'TableColManipCli', '3g', 'plot', 'tableColSelect: tableColSelect <inTable> <outTable> <colName1> [colName2...]'),
|
|
151
|
+
('tableCollapse', 'bridge', 'TableCollapseCli', '3g', 'plot', 'tableCollapse: tableCollapse <inTable> <keyColIndex> <outTable> [hasHeader '),
|
|
152
|
+
('tableMelt', 'direct', 'biocjava.bioDoer.Table.TableMelt', '3g', 'plot', 'tableMelt: tableMelt <inTable> <outTable> # 宽表转长表(第88引擎,TableMelt)'),
|
|
153
|
+
('tableMerge', 'direct', 'biocjava.bioDoer.Table.TableMerger', '3g', 'plot', 'tableMerge: tableMerge --inFileArr "f1,f2,..." --inColIndexArr "0,1,..." --outTable <out> [--defaultNAvalue NA] [--appendMergedKey true|false] [--rmKeyColumns true|false] # 按键合并多个表格(TableMerger;⚠️ ArgsParser 式,旧 docstring 位置参数写法已废弃 N3;位置参数兼容见 _tableMerge_impl)'),
|
|
154
|
+
('tableSplit', 'direct', 'biocjava.bioDoer.Table.TableSplitByCol', '3g', 'plot', 'tableSplit: tableSplit <inTab> <outDir> [--colIndex N] [--suffix .txt]'),
|
|
155
|
+
('tableTranspose', 'direct', 'biocjava.bioDoer.Table.TableTransposer', '3g', 'plot', 'tableTranspose: tableTranspose <inTable> <outTable> # 表格转置(第95引擎,TableTran'),
|
|
156
|
+
('tableUniq', 'direct', 'biocjava.bioDoer.Table.TableUniq', '3g', 'plot', 'tableUniq: tableUniq <inTab> <outFile> [--colIndex N] [--showFreq true|'),
|
|
157
|
+
('tauIndex', 'bridge', 'TauCalcCli', '3g', 'plot', 'tauIndex: tauIndex <inExpTab> <outTAU>'),
|
|
158
|
+
('trimmsa', 'bridge', 'TrimMSACli', '3g', 'plot', 'trimmsa: trimmsa <in.aln.fa> <out.aln.fa> [ratio]'),
|
|
159
|
+
('twoSeqBlast', 'direct', 'biocjava.bioDoer.BLAST.CompareTwoSeqSet', '3g', 'plot', 'twoSeqBlast: twoSeqBlast <query.fa> <subject.fa> <out.txt> [--prog blastp'),
|
|
160
|
+
('upset', 'bridge', 'UpSetCli', '3g', 'plot', 'upset: upset <set1.txt> <set2.txt> [<set3.txt>...] <out.svg> [--min-overlap N] [--rank1 Size|Count|Name] [--rank2 ...] [--rank3 ...] [--size-mode/--count-mode/--name-mode Increasing|Decreasing] # UpSet 集合图(GUI 逆向接口 UpSetPlot.plot,绕 show 弹窗)'),
|
|
161
|
+
('venn2', 'direct', 'biocjava.bioDoer.JJplot2Toolkit.WonderfulVenn.Venn2', '2g', 'plot', 'venn2: venn2 --List1 <setA.txt> --List2 <setB.txt> --label1 A --label2 B --graph <out> --prefix <out> [--bgNum N]'),
|
|
162
|
+
('venn3', 'direct', 'biocjava.bioDoer.JJplot2Toolkit.WonderfulVenn.Venn3', '2g', 'plot', 'venn3: venn3 --List1 <A> --List2 <B> --List3 <C> --label1..3 <labels> --graph <out> --prefix <out>'),
|
|
163
|
+
('venn4', 'direct', 'biocjava.bioDoer.JJplot2Toolkit.WonderfulVenn.Venn4Ellipse', '2g', 'plot', 'venn4: venn4 --List1 <A> --List2 <B> --List3 <C> --List4 <D> --label1..4 <labels> --graph <out> --prefix <out>'),
|
|
164
|
+
('venn5', 'bridge', 'Venn5Cli', '3g', 'plot', 'venn5: venn5 <out> <setA.txt> <setB.txt> <setC.txt> <setD.txt> <setE.txt> [labels]'),
|
|
165
|
+
('venn6', 'bridge', 'Venn6Cli', '3g', 'plot', 'venn6: venn6 <out> <setA..F.txt> [labels]'),
|
|
166
|
+
('violin', 'bridge', 'ViolinCli', '3g', 'plot', 'violin: violin <in.tsv> <out> [width] [height]'),
|
|
167
|
+
('virusRecomb', 'direct', 'biocjava.bioDoer.VirusDetect.RecombinationAnalysis', '3g', 'plot', 'virusRecomb: virusRecomb <inDB.fa> <inContig.fa> <outDir> # 病毒重组分析(第77引'),
|
|
168
|
+
('visualizeblock', 'bridge', 'VisualizeCli', '3g', 'plot', 'visualizeblock: visualizeblock <inBlockOut> <out.pdf> [--labels "Genome1,Gen'),
|
|
169
|
+
('kaks', 'direct', 'biocjava.bioIO.BioSoftPipeServer.PairWiseKaKsCalculator', '2g', 'java', 'kaks --inCDS <cds.fa> --inGenePair <pairs.txt> --outKaks <out.xls> [--inCPU N] [--inPep pep.fa] # 成对 Ka/Ks 计算(GUI 逆向 PairWiseKaKsCalculator;自带 ArgsParser:--key value;inGenePair 为 ID1\\tID2 每行,缺文件时自动全两两配对并翻译 CDS)'),
|
|
170
|
+
('sixframe', 'bridge', 'SixFrameTranlaterCli', '2g', 'java', 'sixframe <in.fa> <out.fa> # 六框翻译(GUI 逆向 #16 SixFrameTranlater:setInFile/setOutFile/process;输出每条序列 6 框 12 条;注意引擎类名拼写 SixFrameTranlater 少一个 s)'),
|
|
171
|
+
('longestorf', 'direct', 'biocjava.bioIO.ORF.GetLongestORF', '2g', 'java', 'longestorf --inFa <seq.fa> --outORFs <out.fa> # 批量最长完整 ORF 预测(GUI 逆向 #17 GetLongestORF:setFastaFile/setOutFile/startPredict;自带 ArgsParser)'),
|
|
172
|
+
('protparam', 'direct', 'biocjava.bioWeb.ProtParamWrapper', '2g', 'java', 'protparam --inFa <pep.fa> --outTab <out.txt> # 蛋白理化性质批量计算(GUI 逆向 #18 ProtParamWrapper.batchCalc;⚠️ 联网 POST Expasy。输出:分子量/pI/不稳定指数/脂肪族指数/GRAVY)'),
|
|
173
|
+
('seqpattern', 'direct', 'biocjava.bioIO.FastX.QuickLocateSeqPattern', '2g', 'java', 'seqpattern --inFasta <seq.fa> --pattern <ATG|regex> --outTab <out.gff3> [--overlap] [--maxSeqLen <len>] # 序列模式定位(GUI 逆向 #20 QuickLocateSeqPattern:正则找模式→GFF3;--key value 空格分隔)'),
|
|
174
|
+
('bed2gff3', 'bridge', 'RegionBedToGFF3Cli', '2g', 'java', 'bed2gff3 <in.bed> <out.gff3> [genome.fa] # exon BED→GFF3(GUI 逆向 #21 RegionBedToGFF3;⚠️ BED 第4列须为 ID:链向:编码 如 G01:+:C;同 ID 多行合并出 mRNA+exon)'),
|
|
175
|
+
('careclassify', 'bridge', 'PlantCAREResultClassifyCli', '2g', 'java', 'careclassify <plantcare.tab> <out.xls> # PlantCARE 顺式元件分类(GUI 逆向 #22;第8列 motif 名查 jar 内置 97 类表,行尾追加大类/亚类;查不到 NA)'),
|
|
176
|
+
('subtree', 'bridge', 'GetSubNewickTreeCli', '2g', 'java', 'subtree <tree.nwk> <idList.txt> <out.nwk> [--contain] # Newick 子树提取(GUI 逆向 #23 GetSubNewickTreeGUIPanel→PhyloTreeMan.getSubTree;--contain 模糊匹配;引擎重算诱导子树内部枝长)'),
|
|
177
|
+
('protsim', 'bridge', 'CalculateSimilarityCli', '2g', 'java', 'protsim <pep.fa> <out.matrix> # 蛋白两两相似度矩阵(GUI 逆向 #24 ProteinPairwiseSimilarityMatrixGUIPanel→CalculateSimilarity;百分比矩阵 TSV)'),
|
|
178
|
+
('iqtree', 'bridge', 'QuickRunIQtreeCli', '2g', 'java', 'iqtree <aln.fa> <outPrefix> [--model MFP] [--ufboot 1000] [--boot N] [--freerate] [--asc] [--threads N] [--redo] # IQ-TREE ML 建树(GUI 逆向 #28 QuickRunIQtree;⚠️ UFBoot 须 ≥1000 否则引擎静默失败;产物 outPrefix.treefile;依赖系统 iqtree)'),
|
|
179
|
+
('trimal', 'bridge', 'QuickTrimALCli', '2g', 'java', 'trimal <in.aln> <out.aln> [--mode gappyout|strict|strictplus|automated1] [--format fasta|clustal|phylip|nexus|mega|nbrf] [--keepheader] # trimAl 比对修剪(GUI 逆向 #29 QuickTrimAL;默认 automated1;依赖系统 trimal;muscle→trimal→iqtree 管线)'),
|
|
180
|
+
('gblocks', 'bridge', 'JgblocksCli', '2g', 'java', 'gblocks <in.aln.fa> <out.aln.fa> [--is 0.5] [--fs 0.85] [--cp 8] [--bl1 15] [--bl2 10] [--nongap 0.5] [--gaptreat none|half|all] # Gblocks 保守区修剪(GUI 逆向 #30 Jgblocks 纯 Java 实现;main 仅 in/out 全参数须 setter;⚠️ 高歧异比对可能 validSites=0 合法)'),
|
|
181
|
+
('goAnno', 'direct', 'biocjava.bioDoer.GeneOntology.Annotation.GoAnnoPipe', '3g', 'java', 'goAnno --IdmappingDb <idmapping.DB.gz> --BlastxAnnoFile <blastx.xml> [--inPutFileType BlastxXml|Query2GiTable] [--maxEvalue 1e-5] [--minQueryCov 0.33] [--outDir dir] [--isDoDbFormat] # GO 注释管道(GUI 逆向 #32 GoAnnotationGUIPanel→GoAnnoPipe;自带 ArgsParser;⚠️ idmappingDb 须 gzip 格式「ID; ID\\tGO:num; GO:num」;产物 outDir/<输入名>.xls)'),
|
|
182
|
+
('fasplit', 'direct', 'biocjava.bioIO.FastX.FastaIndex.QuickSpiltFasta', '2g', 'java', 'fasplit --inFa <in.fa> --outPre <prefix> --NumPerFile <N> [--byCount true] # FASTA 按记录数拆分(GUI 逆向 #33 QuickSpiltFasta;⚠️ 与 filesplit 不同:按记录不切行,产物 prefix.N.split.fa)'),
|
|
183
|
+
('famerge', 'bridge', 'FastaMergerCli', '2g', 'java', 'famerge <out.fa> <in1.fa> <in2.fa> [...] # 多 FASTA 合并(GUI 逆向 #33 FastaMergerAndSpliter.Merge)'),
|
|
184
|
+
('clearchar', 'bridge', 'FileCleanerCli', '2g', 'java', 'clearchar <in.txt> <out.txt> # 文件非法字符清理(GUI 逆向 #34 FileCleaner.simplifyFile;非可打印 ASCII/非 tab→_,空白行跳过,逐行报告)'),
|
|
185
|
+
('gb2fa', 'bridge', 'GenBank2FastaCli', '2g', 'java', 'gb2fa <in.gb> <out.fa> # GenBank→FASTA 转换(GUI 逆向 #36 genBank2Fasta;头含 locus/accession/organism/definition)'),
|
|
186
|
+
('findhomolog', 'direct', 'biocjava.bioIO.BioSoftPipeServer.FindBestHomology', '3g', 'java', 'findhomolog --inQueryProteinSet <query.pep> --inSubjectProteinSet <subject.pep> --targetIDs <ID[,ID2]> --outDir <dir> [--threads N] [--extendClade] [--sensitive N] [--similarity 0.x] [--weightCov 0.x] [--plot] [--directGraph] # 最优同源查找(GUI 逆向 #37 FindBestHomology,同引擎覆盖 GenomeAnnotationSlim+FindBestHomology 两面板;自带 ArgsParser;BLAST+可选建树)'),
|
|
187
|
+
('taxparse', 'bridge', 'TaxonomyBatchCli', '2g', 'java', 'taxparse <idList.txt> <out.xls> # 物种名批量分类解析(GUI 逆向 #38 TaxonomyParserGUIPanel→NCBITaxonomy;⚠️ 联网 NCBI eutils;输出 9 级分类+透传列;单次版=tbtools tool NCBITaxonomy)'),
|
|
188
|
+
('srr2ena', 'bridge', 'GetENALinksCli', '2g', 'java', 'srr2ena <srrList.txt> <out.xls> # SRR→ENA 下载链接解析(GUI 逆向 #39 GetENALinksOfSRR;⚠️ 联网 ENA filereport API+引擎自带 0~3s 限速;17 字段含 fastq_ftp/aspera)'),
|
|
189
|
+
('sraxml2tab', 'direct', 'biocjava.bioIO.SRAtools.ParseSRAXml2Table', '2g', 'java', 'sraxml2tab --sraFullXML <sra.xml> --outTab <out.xls> # SRA XML→信息表(GUI 逆向 #40 ParseSRAXml2Table;自带 ArgsParser;离线 JDOM 解析;XML 从 efetch db=sra 获取)'),
|
|
190
|
+
('sranum2info', 'direct', 'biocjava.bioWeb.EntrezUtils.BatchGetSRARecordInfo', '2g', 'java', 'sranum2info --sraIdList <srrList.txt> --outTabInfo <out.xls> # SRR 批量信息表(GUI 逆向 #41 BatchGetSRARecordInfo;⚠️ 联网 NCBI Entrez+限速;自带 ArgsParser;SRA 组 3/3 全清)'),
|
|
191
|
+
('blat', 'bridge', 'BlatExecutorCli', '2g', 'java', 'blat <db.fa> <query.fa> <out> [--format blast9|psl|pslx|axt|maf|sim4|wublast|blast|blast8] [--minScore N] [--minIdentity 0.x] [--noHead] [--mode auto|dnadna|dnarna] [--tileSize N] [--stepSize N] [--maxGap N] [--maxIntron N] [--extra "opts"] # BLAT 序列比对(GUI 逆向 #42 BlatExecutor;org.ucsc.blat 纯 Java 实现内嵌 jar 无需外部二进制)'),
|
|
192
|
+
('seqrecommend', 'bridge', 'AssemblyRecommandCli', '2g', 'java', 'seqrecommend <genomeSize1n_bp> [--polyploid] [--het 0.01] [--level Minimum|Draft|Haplotyped_Resolved|Haplotyped_T2T] # 基因组组装测序量推荐(GUI 逆向 #43 AssemblyGenomeDataSizeRecommand;纯计算离线;Hifi/HiC 深度+数据量)'),
|
|
193
|
+
('seqfetch', 'direct', 'biocjava.bioWeb.EntrezUtils.NcbiSmartSeqFetchEntrezUtils', '2g', 'java', 'seqfetch --inFile <idList.txt> --outSeqFile <out.fa> --outReport <report.txt> [--targetDb nuccore|protein] [--preferDb db] [--format fasta] [--greedyMode] [--apiKey KEY] [--auditFile f] # NCBI 智能序列下载(GUI 逆向 #44 NcbiSmartSeqFetchEntrezUtils;⚠️ 联网 Entrez+限速;ID 自动检测/转换/审计;支持 apiKey 提速)'),
|
|
194
|
+
('pubmed', 'bridge', 'PubmedSearchCli', '2g', 'java', 'pubmed <query> <out.xls> # PubMed 文献检索汇总(GUI 逆向 #45 PubmedSearch.process;⚠️ 联网 eutils;输出期刊/标题/年份/IF/DOI 表)'),
|
|
195
|
+
# N13: gwas 分组补命令(vcfAddID/mimicVqsr 原只在 CLI_TOOLS,gwas 分组是空壳)
|
|
196
|
+
('vcfAddID', 'direct', 'biocjava.bioDoer.GWAS.VCFAddID', '2g', 'java', 'vcfAddID: vcfAddID --inFile <vcf> --outFile <out.vcf> # VCF 加 ID 列(GWAS;ArgsParser --inFile/--outFile,支持 .gz)'),
|
|
197
|
+
('mimicVqsr', 'direct', 'biocjava.bioDoer.GWAS.MimicVqsrCutoffFind', '2g', 'java', 'mimicVqsr: mimicVqsr --inFile <vcf> --outFile <out.txt> # VCF 质量指标(QD/MQ/FS/SOR;GWAS)'),
|
|
198
|
+
]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
_IMPL_REGISTRY = {} # 第四轮评审: 显式 impl 注册表(替代隐式 globals 注入)
|
|
203
|
+
|
|
204
|
+
def parse_kv_args(args, spec, raw_flags=()):
|
|
205
|
+
"""通用参数解析(替代各 impl 重复的 while 循环)。
|
|
206
|
+
|
|
207
|
+
spec: {flag: (attr, converter)} —— 取值 flag,converter 可选(如 int);
|
|
208
|
+
raw_flags: 出现即收集到 raw 的 flag(如 --super5)。
|
|
209
|
+
返回 (kwargs, positional, raw);kwargs 只含出现的 flag,缺省值由调用方自行默认。
|
|
210
|
+
"""
|
|
211
|
+
kwargs, pos, raw = {}, [], []
|
|
212
|
+
i = 0
|
|
213
|
+
n = len(args)
|
|
214
|
+
while i < n:
|
|
215
|
+
a = args[i]
|
|
216
|
+
if a in spec and i + 1 < n:
|
|
217
|
+
attr, conv = spec[a]
|
|
218
|
+
val = args[i + 1]
|
|
219
|
+
kwargs[attr] = conv(val) if conv else val
|
|
220
|
+
i += 2
|
|
221
|
+
elif a in raw_flags:
|
|
222
|
+
raw.append(a)
|
|
223
|
+
i += 1
|
|
224
|
+
else:
|
|
225
|
+
pos.append(a)
|
|
226
|
+
i += 1
|
|
227
|
+
return kwargs, pos, raw
|
|
228
|
+
|
|
229
|
+
def _warn_gtf_input(args, cmd):
|
|
230
|
+
"""N28/N22/N36: Gxf 族命令输入格式预检——GTF 引擎 NPE、BED 误报 GFF3/GTF 识别。"""
|
|
231
|
+
for a in args:
|
|
232
|
+
if not a.startswith("-") and a.lower().endswith((".gtf", ".gtf.gz")):
|
|
233
|
+
print(f"⚠️ 格式提醒: {cmd} 对 GTF 输入支持有限(GENCODE 真 GTF 会触发引擎 NPE,N28)", file=sys.stderr)
|
|
234
|
+
print(" (建议先转 GFF3 再输入;如确认可忽略)", file=sys.stderr)
|
|
235
|
+
break
|
|
236
|
+
if not a.startswith("-") and a.lower().endswith((".bed", ".bed.gz")):
|
|
237
|
+
# N22/N36: 引擎 GFF3/GTF 识别对 BED 报误导性错误("can not decide")+ GxfStat NPE
|
|
238
|
+
print(f"⚠️ 格式提醒: {cmd} 不支持 BED 输入(引擎会报『can not decide GFF3 or GTF』并可能 NPE)", file=sys.stderr)
|
|
239
|
+
print(" (请提供 GFF3/GTF 注释文件)", file=sys.stderr)
|
|
240
|
+
break
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
# N32: 无参调用会挂起/弹窗的命令(引擎无参读 stdin/等 GUI)——直接打印用法退出
|
|
244
|
+
_NOARG_HANG = {"gel"}
|
|
245
|
+
|
|
246
|
+
def _make_impl(cmd, kind, cls, xmx, runner, doc):
|
|
247
|
+
"""工厂:按注册表条目生成 _xxx_impl 闭包(保持 (args, verbose, quiet) 签名)"""
|
|
248
|
+
# N28: Gxf 族引擎对 GTF 输入解析 NPE(GENCODE 真 GTF 实证),输入预检警告
|
|
249
|
+
gxf_cmd = cmd.startswith("gxf") or cmd in ("gsadiag", "annocompare", "genedensity", "gblocks")
|
|
250
|
+
if kind == "bridge":
|
|
251
|
+
def impl(args, verbose=False, quiet=False):
|
|
252
|
+
if cmd in _NOARG_HANG and not args:
|
|
253
|
+
print(f"用法: {doc.split(':', 1)[1].strip() if ':' in doc else cmd}", file=sys.stderr)
|
|
254
|
+
return 1
|
|
255
|
+
if gxf_cmd:
|
|
256
|
+
_warn_gtf_input(args, cmd)
|
|
257
|
+
ensure_bridge(cls)
|
|
258
|
+
java_args = ["java", f"-Xmx{xmx}", "-cp", cp(BUILD_DIR, JAR), cls] + args
|
|
259
|
+
if runner == "plot":
|
|
260
|
+
return run_plot(java_args, verbose=verbose, quiet=quiet, command_name=cmd)
|
|
261
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name=cmd)
|
|
262
|
+
else: # direct
|
|
263
|
+
def impl(args, verbose=False, quiet=False):
|
|
264
|
+
if cmd in _NOARG_HANG and not args:
|
|
265
|
+
print(f"用法: {doc.split(':', 1)[1].strip() if ':' in doc else cmd}", file=sys.stderr)
|
|
266
|
+
return 1
|
|
267
|
+
if gxf_cmd:
|
|
268
|
+
_warn_gtf_input(args, cmd)
|
|
269
|
+
# N27: direct 类也含 build/(fake jaxb DatatypeConverter 等),否则 JDK9+ 缺 javax.xml.bind
|
|
270
|
+
java_args = ["java", f"-Xmx{xmx}", "-cp", cp(BUILD_DIR, JAR), cls] + args
|
|
271
|
+
if runner == "plot":
|
|
272
|
+
return run_plot(java_args, verbose=verbose, quiet=quiet, command_name=cmd)
|
|
273
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name=cmd)
|
|
274
|
+
impl.__doc__ = doc
|
|
275
|
+
impl.__name__ = f"_{cmd}_impl"
|
|
276
|
+
return impl
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
_HANDWRITTEN = {} if False else None # placeholder
|
|
280
|
+
for _cmd, _kind, _cls, _xmx, _runner, _doc in ENGINE_REGISTRY:
|
|
281
|
+
if hasattr(sys.modules[__name__], f"_{_cmd}_impl"):
|
|
282
|
+
# 手写实现在 ENGINE_REGISTRY 后同文件定义时会覆盖注册; 冲突条目应在注册表删除(mirnatarget/msy 已删)
|
|
283
|
+
raise SystemError(f"注册表与手写实现冲突: {_cmd}(手写 {f'_{_cmd}_impl'} 存在)——请从 ENGINE_REGISTRY 删除该条目")
|
|
284
|
+
# 设计说明(第三轮审查评估): 表驱动工厂统一产出 _<name>_impl,cli_load 按名查找;
|
|
285
|
+
# 命令清单的单一数据源是 ENGINE_REGISTRY,command_metadata.json 是其投影(gen_metadata 生成,--check 防漂移),
|
|
286
|
+
# 不再反向依赖 JSON 生成 click(避免运行时依赖生成物、且 JSON 不含 runner/xmx/doc 等运行时信息)。
|
|
287
|
+
# 第四轮评审: 显式注册表(替代隐式 globals 注入),cli_load 优先查 _IMPL_REGISTRY
|
|
288
|
+
_fn = _make_impl(_cmd, _kind, _cls, _xmx, _runner, _doc)
|
|
289
|
+
_IMPL_REGISTRY[_cmd] = _fn
|
|
290
|
+
globals()[f"_{_cmd}_impl"] = _fn
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# ── 特殊实现(手写保留)──────────────────────────────────────────
|
|
294
|
+
|
|
295
|
+
def _hmmsearch_impl(args, verbose=False, quiet=False):
|
|
296
|
+
"""hmmsearch: hmmsearch <pfamA.hmm> <target.pep> <idList.txt> <out.txt> # HMM Search 域扫描(= simpleHmmscan 引擎,调系统 hmmsearch,G1 补齐别名)"""
|
|
297
|
+
return _simplehmmscan_impl(args, verbose=verbose, quiet=quiet) # type: ignore[name-defined] # noqa: F821 # 注册表动态生成
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _gxfAttr_impl(args, verbose=False, quiet=False):
|
|
301
|
+
"""gxfAttr: gxfAttr <in.gff3|gtf> <out.tsv> [--feature mRNA] [--attrs ID,Name,Parent] # GXF 属性/ID 对照表提取(G7 补齐,Python 原生,jar 无此引擎)"""
|
|
302
|
+
import re as _re
|
|
303
|
+
# 解析参数
|
|
304
|
+
pos, feature, attrs = [], "mRNA", None
|
|
305
|
+
_kw, pos, _ = parse_kv_args(args, {
|
|
306
|
+
"--feature": ("feature", None),
|
|
307
|
+
"--attrs": ("attrs", lambda s: [a.strip() for a in s.split(",") if a.strip()])})
|
|
308
|
+
feature = _kw.get("feature", "mRNA"); attrs = _kw.get("attrs")
|
|
309
|
+
if len(pos) < 2:
|
|
310
|
+
print("用法: gxfAttr <in.gff3|gtf> <out.tsv> [--feature mRNA] [--attrs ID,Name,Parent]", file=sys.stderr)
|
|
311
|
+
return 1
|
|
312
|
+
in_gxf, out_tsv = pos[0], pos[1]
|
|
313
|
+
if not os.path.isfile(in_gxf):
|
|
314
|
+
print(f"❌ 输入文件不存在: {in_gxf}", file=sys.stderr)
|
|
315
|
+
return 2
|
|
316
|
+
def parse_attrs(field):
|
|
317
|
+
"""兼容 GFF3 (k=v;) 与 GTF (k "v";) 属性列"""
|
|
318
|
+
d = {}
|
|
319
|
+
for m in _re.finditer(r'([^\s;=]+)\s*=\s*"?([^";]+)"?\s*;?', field):
|
|
320
|
+
d[m.group(1)] = m.group(2)
|
|
321
|
+
if not d: # GTF 风格
|
|
322
|
+
for m in _re.finditer(r'([^\s;]+)\s+"([^"]+)"\s*;?', field):
|
|
323
|
+
d[m.group(1)] = m.group(2)
|
|
324
|
+
return d
|
|
325
|
+
rows, attr_keys = [], []
|
|
326
|
+
with open(in_gxf, encoding="utf-8", errors="replace") as f:
|
|
327
|
+
for line in f:
|
|
328
|
+
if line.startswith("#") or not line.strip():
|
|
329
|
+
continue
|
|
330
|
+
parts = line.rstrip("\n").split("\t")
|
|
331
|
+
if len(parts) < 9:
|
|
332
|
+
continue
|
|
333
|
+
feat = parts[2]
|
|
334
|
+
if feature.lower() != "all" and feat.lower() != feature.lower():
|
|
335
|
+
continue
|
|
336
|
+
d = parse_attrs(parts[8])
|
|
337
|
+
d["__feature__"] = feat
|
|
338
|
+
d["__chr__"], d["__start__"], d["__end__"], d["__strand__"] = parts[0], parts[3], parts[4], parts[6]
|
|
339
|
+
rows.append(d)
|
|
340
|
+
for k in d:
|
|
341
|
+
if not k.startswith("__") and k not in attr_keys:
|
|
342
|
+
attr_keys.append(k)
|
|
343
|
+
if not rows:
|
|
344
|
+
print(f"⚠️ 未提取到 feature={feature} 的记录(--feature all 提取全部)", file=sys.stderr)
|
|
345
|
+
return 1
|
|
346
|
+
# 列序: feature/坐标 + 用户指定 attrs + 其余按出现序
|
|
347
|
+
lead = ["__feature__", "__chr__", "__start__", "__end__", "__strand__"]
|
|
348
|
+
if attrs:
|
|
349
|
+
ordered = attrs + [k for k in attr_keys if k not in attrs]
|
|
350
|
+
else:
|
|
351
|
+
preferred = ["ID", "Name", "Parent", "gene_id", "gene_name", "transcript_id", "symbol", "gene", "product", "Note"]
|
|
352
|
+
ordered = [k for k in preferred if k in attr_keys] + [k for k in attr_keys if k not in preferred]
|
|
353
|
+
header = ["feature", "chr", "start", "end", "strand"] + ordered
|
|
354
|
+
with open(out_tsv, "w", encoding="utf-8") as f:
|
|
355
|
+
f.write("\t".join(header) + "\n")
|
|
356
|
+
for d in rows:
|
|
357
|
+
f.write("\t".join([d.get(k, "") for k in lead] + [d.get(k, "") for k in ordered]) + "\n")
|
|
358
|
+
if not quiet:
|
|
359
|
+
print(f"[gxfAttr] {len(rows)} 条 {feature} 记录 × {len(ordered)} 属性列 → {out_tsv}", file=sys.stderr)
|
|
360
|
+
return 0
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _kallisto_impl(args, verbose=False, quiet=False):
|
|
364
|
+
"""kallisto: kallisto <transcriptome.fa> <reads.fq[,reads2.fq]> <outAbundance> [--kmer N] [--threads N] [--bootstrap N] [--bias] [--single] [--frag-len N] [--frag-sd N] # RNA-seq 定量(插件 P00740 CLI 化,直调 kallisto 二进制——插件 wrapper 的 Linux 分支有拼接 bug 已绕开)"""
|
|
365
|
+
# A(kallisto Windows 二进制选择, V1 §3 P1-3): 按平台优先 .exe(Linux ELF 在 Windows 报 WinError 193)
|
|
366
|
+
bin_path = os.path.join(ROOT, "plugins", "lib", "bin",
|
|
367
|
+
"kallisto.exe" if os.name == "nt" else "kallisto")
|
|
368
|
+
if not os.path.isfile(bin_path) and os.name == "nt":
|
|
369
|
+
bin_path = os.path.join(ROOT, "plugins", "lib", "bin", "kallisto") # 回退旧路径
|
|
370
|
+
if not os.path.isfile(bin_path):
|
|
371
|
+
print(f"❌ kallisto 二进制缺失: {bin_path}", file=sys.stderr)
|
|
372
|
+
return 1
|
|
373
|
+
libs_dir = os.path.join(ROOT, "plugins", "lib", "kallisto-libs")
|
|
374
|
+
# 解析参数
|
|
375
|
+
pos, kmer, threads, bootstrap, bias, single, frag_len, frag_sd = [], 31, 4, 0, False, False, 200, 30
|
|
376
|
+
_kw, pos, _raw = parse_kv_args(args, {
|
|
377
|
+
"--kmer": ("kmer", int), "--threads": ("threads", int),
|
|
378
|
+
"--bootstrap": ("bootstrap", int), "--frag-len": ("frag_len", int),
|
|
379
|
+
"--frag-sd": ("frag_sd", int)}, raw_flags=("--bias", "--single"))
|
|
380
|
+
kmer = _kw.get("kmer", 21); threads = _kw.get("threads", 2)
|
|
381
|
+
bootstrap = _kw.get("bootstrap", 100); frag_len = _kw.get("frag_len", 400)
|
|
382
|
+
frag_sd = _kw.get("frag_sd", 20); bias = "--bias" in _raw; single = "--single" in _raw
|
|
383
|
+
if len(pos) < 3:
|
|
384
|
+
print("用法: kallisto <transcriptome.fa> <reads.fq[,reads2.fq]> <outAbundance> [--kmer N] [--threads N] [--bootstrap N] [--bias] [--single] [--frag-len N] [--frag-sd N]", file=sys.stderr)
|
|
385
|
+
return 1
|
|
386
|
+
tx, reads_str, out_ab = pos[0], pos[1], pos[2]
|
|
387
|
+
reads = [r.strip() for r in reads_str.split(",")]
|
|
388
|
+
import shutil
|
|
389
|
+
env = dict(os.environ)
|
|
390
|
+
env["PATH"] = os.path.dirname(bin_path) + os.pathsep + env.get("PATH", "")
|
|
391
|
+
if os.path.isdir(libs_dir):
|
|
392
|
+
env["LD_LIBRARY_PATH"] = libs_dir + os.pathsep + env.get("LD_LIBRARY_PATH", "")
|
|
393
|
+
idx = os.path.join(os.path.dirname(os.path.abspath(out_ab)), os.path.basename(out_ab) + ".kallisto.idx")
|
|
394
|
+
try:
|
|
395
|
+
# 1) index
|
|
396
|
+
os.unlink(idx)
|
|
397
|
+
except OSError:
|
|
398
|
+
pass
|
|
399
|
+
r = subprocess.run([bin_path, "index", "-k", str(kmer), "-i", idx, tx], env=env, capture_output=True, text=True)
|
|
400
|
+
if r.returncode != 0:
|
|
401
|
+
print(f"❌ kallisto index 失败:{chr(10)}{r.stderr[-800:]}", file=sys.stderr)
|
|
402
|
+
return r.returncode
|
|
403
|
+
# 2) quant
|
|
404
|
+
tmp_dir = tempfile.mkdtemp(prefix="kallisto_quant_")
|
|
405
|
+
try:
|
|
406
|
+
q = [bin_path, "quant", "-i", idx, "-o", tmp_dir, "-t", str(threads)]
|
|
407
|
+
if bias: q.append("--bias")
|
|
408
|
+
if bootstrap: q += ["-b", str(bootstrap)]
|
|
409
|
+
if single:
|
|
410
|
+
q += ["--single", "-l", str(frag_len), "-s", str(frag_sd)]
|
|
411
|
+
q += reads
|
|
412
|
+
r = subprocess.run(q, env=env, capture_output=True, text=True)
|
|
413
|
+
if r.returncode != 0:
|
|
414
|
+
print(f"❌ kallisto quant 失败:{chr(10)}{r.stderr[-800:]}", file=sys.stderr)
|
|
415
|
+
return r.returncode
|
|
416
|
+
abund = os.path.join(tmp_dir, "abundance.tsv")
|
|
417
|
+
if not os.path.isfile(abund):
|
|
418
|
+
print("❌ 未找到 abundance.tsv(quant 输出异常)", file=sys.stderr)
|
|
419
|
+
return 1
|
|
420
|
+
shutil.copy2(abund, out_ab)
|
|
421
|
+
if not quiet:
|
|
422
|
+
print(f"[kallisto] {os.path.basename(out_ab)} 已写出(kmer={kmer} threads={threads}{' bias' if bias else ''}{' single' if single else ''})", file=sys.stderr)
|
|
423
|
+
return 0
|
|
424
|
+
finally:
|
|
425
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
426
|
+
try: os.unlink(idx)
|
|
427
|
+
except OSError as _e:
|
|
428
|
+
logging.getLogger(__name__).debug("io: %s", _e) # 静默容错(第五轮评审:留痕)
|
|
429
|
+
pass
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def _fimo_impl(args, verbose=False, quiet=False):
|
|
433
|
+
"""fimo: fimo --o <outDir> <motifs.meme> <promoter.fa> # MEME FIMO motif 扫描(插件 P00552 等价,直调系统 fimo;meme-suite)"""
|
|
434
|
+
import shutil as _sh
|
|
435
|
+
fimo_bin = _sh.which("fimo")
|
|
436
|
+
if not fimo_bin:
|
|
437
|
+
print("❌ 未找到 fimo(meme-suite),安装: apt install meme-suite", file=sys.stderr)
|
|
438
|
+
return 1
|
|
439
|
+
r = subprocess.run([fimo_bin] + args, capture_output=True, text=True)
|
|
440
|
+
if r.returncode != 0:
|
|
441
|
+
print(f"❌ fimo 失败:\n{r.stderr[-600:]}", file=sys.stderr)
|
|
442
|
+
return r.returncode
|
|
443
|
+
if not quiet:
|
|
444
|
+
print("[fimo] 完成 (退出码 0)", file=sys.stderr)
|
|
445
|
+
return 0
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def _xml2blasttab_impl(args, verbose=False, quiet=False):
|
|
449
|
+
"""xml2blasttab: xml2blasttab <in.xml> <out.txt> # BLAST XML→标准 12 列表(GUI 逆向 #25 BlastXmlToBlastFoolTable.xml2ShowerTable;QueryID/SubjectID/Identity/E-value/BitScore...)"""
|
|
450
|
+
ensure_bridge("BlastXmlConvertCli")
|
|
451
|
+
java_args = ["java", "-Xmx2g", "-cp", cp(BUILD_DIR, JAR), "BlastXmlConvertCli", "blasttab"] + args
|
|
452
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="xml2blasttab")
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def _xml2pairwise_impl(args, verbose=False, quiet=False):
|
|
456
|
+
"""xml2pairwise: xml2pairwise <in.xml> <out.txt> # BLAST XML→网页 pairwise 对齐文本(GUI 逆向 #25 BlastXMLToPairwise.parse;⚠️ 需 Hsp_query-frame/Hsp_hit-frame 字段)"""
|
|
457
|
+
ensure_bridge("BlastXmlConvertCli")
|
|
458
|
+
java_args = ["java", "-Xmx2g", "-cp", cp(BUILD_DIR, JAR), "BlastXmlConvertCli", "pairwise"] + args
|
|
459
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="xml2pairwise")
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def _fa2tab_impl(args, verbose=False, quiet=False):
|
|
463
|
+
"""fa2tab: fa2tab <in.fa> <out.tab> # FASTA→表格 ID\\t序列(GUI 逆向 #26 FastaTable.fa2tab;与 tab2fa 往返一致)"""
|
|
464
|
+
ensure_bridge("FastaTableConvertCli")
|
|
465
|
+
java_args = ["java", "-Xmx2g", "-cp", cp(BUILD_DIR, JAR), "FastaTableConvertCli", "fa2tab"] + args
|
|
466
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="fa2tab")
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def _tab2fa_impl(args, verbose=False, quiet=False):
|
|
470
|
+
"""tab2fa: tab2fa <in.tab> <out.fa> # 表格→FASTA(GUI 逆向 #26 FastaTable.tab2fa)"""
|
|
471
|
+
ensure_bridge("FastaTableConvertCli")
|
|
472
|
+
java_args = ["java", "-Xmx2g", "-cp", cp(BUILD_DIR, JAR), "FastaTableConvertCli", "tab2fa"] + args
|
|
473
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="tab2fa")
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _muscle_impl(args, verbose=False, quiet=False):
|
|
477
|
+
"""muscle: muscle <in.fa> <out.aln> [--super5] [--threads N] # MUSCLE 多序列比对(GUI 逆向 #27 MuscleGUIPanel→QuickRunMUSCLE;⚠️ 引擎硬编码 muscle3 -in/-out 语法在 v5 系统崩 → Python 直调自动适配;依赖系统 muscle)"""
|
|
478
|
+
import shutil as _sh
|
|
479
|
+
muscle_bin = _sh.which("muscle")
|
|
480
|
+
if not muscle_bin:
|
|
481
|
+
print("❌ 未找到 muscle,安装: apt install muscle", file=sys.stderr)
|
|
482
|
+
return 1
|
|
483
|
+
pos, extra = [], []
|
|
484
|
+
i = 0
|
|
485
|
+
while i < len(args):
|
|
486
|
+
a = args[i]
|
|
487
|
+
if a in ("--super5",):
|
|
488
|
+
extra.append(a); i += 1
|
|
489
|
+
elif a == "--threads" and i + 1 < len(args):
|
|
490
|
+
extra += ["-threads", args[i + 1]]; i += 2
|
|
491
|
+
else:
|
|
492
|
+
pos.append(a); i += 1
|
|
493
|
+
if len(pos) < 2:
|
|
494
|
+
print("用法: muscle <in.fa> <out.aln> [--super5] [--threads N]", file=sys.stderr)
|
|
495
|
+
return 1
|
|
496
|
+
in_fa, out_aln = pos[0], pos[1]
|
|
497
|
+
if not os.path.isfile(in_fa):
|
|
498
|
+
print(f"❌ 输入文件不存在: {in_fa}", file=sys.stderr)
|
|
499
|
+
return 2
|
|
500
|
+
# 版本探测:muscle -version 输出含 "muscle 5"/"MUSCLE v5" 即 v5 语法
|
|
501
|
+
vr = subprocess.run([muscle_bin, "-version"], capture_output=True, text=True)
|
|
502
|
+
vtxt = (vr.stdout + vr.stderr).lower()
|
|
503
|
+
is_v5 = "muscle 5" in vtxt or "muscle v5" in vtxt or "v5." in vtxt
|
|
504
|
+
super5 = "--super5" in extra
|
|
505
|
+
if is_v5:
|
|
506
|
+
cmd = [muscle_bin, "-super5" if super5 else "-align", in_fa, "-output", out_aln]
|
|
507
|
+
cmd += [e for e in extra if e != "--super5"]
|
|
508
|
+
else:
|
|
509
|
+
cmd = [muscle_bin, "-in", in_fa, "-out", out_aln]
|
|
510
|
+
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
511
|
+
if r.returncode != 0 or not os.path.isfile(out_aln):
|
|
512
|
+
print(f"❌ muscle 失败:\n{r.stderr[-500:]}", file=sys.stderr)
|
|
513
|
+
return r.returncode or 1
|
|
514
|
+
if not quiet:
|
|
515
|
+
n = sum(1 for l in open(out_aln) if l.startswith(">"))
|
|
516
|
+
print(f"[muscle] 比对完成({'v5' if is_v5 else 'v3'} 语法): {n} 条 → {out_aln}", file=sys.stderr)
|
|
517
|
+
return 0
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _bestid_impl(args, verbose=False, quiet=False):
|
|
521
|
+
"""bestid: bestid --inQuery <query.pep> --Subject <subject.pep> --OutPrefix <outPrefix> [--useDiamond] [--threads N] # 双向 BLAST 最优 ID 转换(GUI 逆向 #31 BestIDConverter;RBH 互撞 Excellent/Poor;⚠️ 引擎强制 --threads,CLI 公共 -t 会吃掉它——缺省自动注入 4,精确指定用 --config parameter.txt;依赖 blastp/diamond)"""
|
|
522
|
+
if "--threads" not in args:
|
|
523
|
+
args = args + ["--threads", "4"]
|
|
524
|
+
java_args = ["java", "-Xmx3g", "-cp", JAR,
|
|
525
|
+
"biocjava.bioDoer.BLAST.ReciprocalBlast.BestIDConverter"] + args
|
|
526
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="bestid")
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def _getseqdb_impl(args, verbose=False, quiet=False):
|
|
530
|
+
"""getseqdb: getseqdb <dbPrefix> <idList.txt> <out.fa> [--entry ID] # 从 BLAST 库批量提取序列(GUI 逆向 #35 GetSeqFromBlastDBGUIPanel $5:blastdbcmd -db X -entry_batch ids -out Y;⚠️ 库须 makeblastdb -parse_seqids 建,否则 Skipped;依赖系统 blastdbcmd)"""
|
|
531
|
+
import shutil as _sh
|
|
532
|
+
blastdbcmd = _sh.which("blastdbcmd")
|
|
533
|
+
if not blastdbcmd:
|
|
534
|
+
print("❌ 未找到 blastdbcmd(ncbi-blast+),安装: apt install ncbi-blast+", file=sys.stderr)
|
|
535
|
+
return 1
|
|
536
|
+
pos, entry = [], None
|
|
537
|
+
i = 0
|
|
538
|
+
while i < len(args):
|
|
539
|
+
if args[i] == "--entry" and i + 1 < len(args):
|
|
540
|
+
entry = args[i + 1]; i += 2
|
|
541
|
+
else:
|
|
542
|
+
pos.append(args[i]); i += 1
|
|
543
|
+
if entry:
|
|
544
|
+
if len(pos) < 2:
|
|
545
|
+
print("用法: getseqdb <dbPrefix> <out.fa> --entry ID", file=sys.stderr)
|
|
546
|
+
return 1
|
|
547
|
+
cmd = [blastdbcmd, "-db", pos[0], "-entry", entry, "-out", pos[1]]
|
|
548
|
+
out_fa = pos[1]
|
|
549
|
+
else:
|
|
550
|
+
if len(pos) < 3:
|
|
551
|
+
print("用法: getseqdb <dbPrefix> <idList.txt> <out.fa> [--entry ID]", file=sys.stderr)
|
|
552
|
+
return 1
|
|
553
|
+
if not os.path.isfile(pos[1]):
|
|
554
|
+
print(f"❌ ID 列表不存在: {pos[1]}", file=sys.stderr)
|
|
555
|
+
return 2
|
|
556
|
+
cmd = [blastdbcmd, "-db", pos[0], "-entry_batch", pos[1], "-out", pos[2]]
|
|
557
|
+
out_fa = pos[2]
|
|
558
|
+
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
559
|
+
if r.returncode != 0 or not os.path.isfile(out_fa):
|
|
560
|
+
print(f"❌ blastdbcmd 失败:\n{r.stderr[-400:]}", file=sys.stderr)
|
|
561
|
+
return r.returncode or 1
|
|
562
|
+
if not quiet:
|
|
563
|
+
n = sum(1 for l in open(out_fa) if l.startswith(">"))
|
|
564
|
+
print(f"[getseqdb] 提取 {n} 条 → {out_fa}", file=sys.stderr)
|
|
565
|
+
return 0
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def _genomefilter_impl(args, verbose=False, quiet=False):
|
|
569
|
+
"""genomefilter: genomefilter <in.fa> <out.fa> --min-len <N> [--gxf <in.gff3>] # 按序列长度过滤(GUI 逆向 #19 GenomeLengthFilterGUIPanel:QuickStatFasta 统计 → 按 minLen 过滤 ID → ExtractFasta 提取;可选 GXF 同过滤)"""
|
|
570
|
+
pos, min_len, gxf = [], None, None
|
|
571
|
+
_kw, pos, _ = parse_kv_args(args, {"--min-len": ("min_len", int), "--gxf": ("gxf", None)})
|
|
572
|
+
min_len = _kw.get("min_len"); gxf = _kw.get("gxf")
|
|
573
|
+
if len(pos) < 2 or min_len is None:
|
|
574
|
+
print("用法: genomefilter <in.fa> <out.fa> --min-len <N> [--gxf <in.gff3>]", file=sys.stderr)
|
|
575
|
+
return 1
|
|
576
|
+
in_fa, out_fa = pos[0], pos[1]
|
|
577
|
+
if not os.path.isfile(in_fa):
|
|
578
|
+
print(f"❌ 输入文件不存在: {in_fa}", file=sys.stderr)
|
|
579
|
+
return 2
|
|
580
|
+
# 1) QuickStatFasta 统计长度
|
|
581
|
+
stat_out = os.path.join(tempfile.gettempdir(), f"genomefilter_stat_{os.getpid()}.txt")
|
|
582
|
+
r = subprocess.run(["java", "-Xmx2g", "-cp", JAR,
|
|
583
|
+
"biocjava.bioIO.FastX.FastaIndex.QuickStatFasta",
|
|
584
|
+
"--inFasta", in_fa, "--outPutFile", stat_out],
|
|
585
|
+
capture_output=True, text=True)
|
|
586
|
+
if r.returncode != 0 or not os.path.isfile(stat_out):
|
|
587
|
+
print(f"❌ statFasta 失败:\n{r.stderr[-400:]}", file=sys.stderr)
|
|
588
|
+
return r.returncode or 1
|
|
589
|
+
# 2) 过滤 ID(表头: Original_ID Simplified_ID ... Length)
|
|
590
|
+
keep_ids = []
|
|
591
|
+
with open(stat_out, encoding="utf-8", errors="replace") as f:
|
|
592
|
+
header = f.readline().rstrip("\n").split("\t")
|
|
593
|
+
try:
|
|
594
|
+
len_idx = header.index("Length")
|
|
595
|
+
id_idx = 0
|
|
596
|
+
except ValueError:
|
|
597
|
+
len_idx, id_idx = 3, 0
|
|
598
|
+
for line in f:
|
|
599
|
+
if not line.strip():
|
|
600
|
+
continue
|
|
601
|
+
cols = line.rstrip("\n").split("\t")
|
|
602
|
+
if len(cols) <= len_idx:
|
|
603
|
+
continue
|
|
604
|
+
try:
|
|
605
|
+
ln = int(cols[len_idx])
|
|
606
|
+
except ValueError:
|
|
607
|
+
continue
|
|
608
|
+
if ln >= min_len:
|
|
609
|
+
keep_ids.append(cols[id_idx])
|
|
610
|
+
if not keep_ids:
|
|
611
|
+
print(f"⚠️ 无序列 ≥ {min_len} bp,输出为空", file=sys.stderr)
|
|
612
|
+
return 1
|
|
613
|
+
id_list = os.path.join(tempfile.gettempdir(), f"genomefilter_ids_{os.getpid()}.txt")
|
|
614
|
+
with open(id_list, "w", encoding="utf-8") as f:
|
|
615
|
+
f.write("\n".join(keep_ids) + "\n")
|
|
616
|
+
# 3) ExtractFasta 按 ID 提取
|
|
617
|
+
r = subprocess.run(["java", "-Xmx2g", "-cp", JAR,
|
|
618
|
+
"biocjava.bioDoer.Fasta.ExtractFasta",
|
|
619
|
+
"--inFa", in_fa, "--inIDList", id_list, "--outFa", out_fa,
|
|
620
|
+
"--processMode", "Extract", "--matchMode", "Match", "--caseInSensitive", "false"],
|
|
621
|
+
capture_output=True, text=True)
|
|
622
|
+
os.unlink(id_list)
|
|
623
|
+
try: os.unlink(stat_out)
|
|
624
|
+
except OSError as _e:
|
|
625
|
+
logging.getLogger(__name__).debug("io: %s", _e)
|
|
626
|
+
pass
|
|
627
|
+
if r.returncode != 0 or not os.path.isfile(out_fa):
|
|
628
|
+
print(f"❌ ExtractFasta 失败:\n{r.stderr[-400:]}", file=sys.stderr)
|
|
629
|
+
return r.returncode or 1
|
|
630
|
+
# 4) 可选 GXF 同过滤
|
|
631
|
+
if gxf:
|
|
632
|
+
if os.path.isfile(gxf):
|
|
633
|
+
keep_set = set(keep_ids)
|
|
634
|
+
out_gxf = out_fa + ".gxf"
|
|
635
|
+
with open(gxf, encoding="utf-8", errors="replace") as fi, open(out_gxf, "w", encoding="utf-8") as fo:
|
|
636
|
+
for line in fi:
|
|
637
|
+
if not line.strip() or line.startswith("#"):
|
|
638
|
+
continue
|
|
639
|
+
chr_name = line.split("\t", 1)[0].strip()
|
|
640
|
+
if chr_name in keep_set:
|
|
641
|
+
fo.write(line)
|
|
642
|
+
if not quiet:
|
|
643
|
+
print(f"[genomefilter] GXF 同过滤: {out_gxf}", file=sys.stderr)
|
|
644
|
+
else:
|
|
645
|
+
print(f"⚠️ GXF 文件不存在,跳过: {gxf}", file=sys.stderr)
|
|
646
|
+
if not quiet:
|
|
647
|
+
print(f"[genomefilter] {len(keep_ids)}/{len(open(in_fa).readlines())} 序列保留 → {out_fa}", file=sys.stderr)
|
|
648
|
+
return 0
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def _notung_impl(args, verbose=False, quiet=False):
|
|
652
|
+
"""notung: notung <gene.nwk> -s <species.nwk> --reconcile [Notung 原生参数] # 基因树-物种树 reconcile(duplication/loss 推断,插件 P00651 CLI 化)"""
|
|
653
|
+
notung = os.path.join(ROOT, "plugins", "lib", "Notung-2.9.1.5.jar")
|
|
654
|
+
if not os.path.isfile(notung):
|
|
655
|
+
print(f"❌ Notung 引擎缺失: {notung}", file=sys.stderr)
|
|
656
|
+
return 1
|
|
657
|
+
java_args = ["java", "-Xmx2g", "-jar", notung] + args
|
|
658
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="notung")
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
def _newickRename_impl(args, verbose=False, quiet=False):
|
|
662
|
+
"""newickRename: newickRename --inNwk <tree.nwk> --renameMap <map.tsv> --outNwk <out.nwk> # 树叶批量重命名(插件 P00690 CLI 化,map 为 OldName\\tNewName)"""
|
|
663
|
+
pjar = os.path.join(ROOT, "plugins", "lib", "Plugin_NewickRenamer.jar")
|
|
664
|
+
if not os.path.isfile(pjar):
|
|
665
|
+
print(f"❌ 插件缺失: {pjar}", file=sys.stderr)
|
|
666
|
+
return 1
|
|
667
|
+
java_args = ["java", "-Xmx1g", "-cp", f"{pjar}:{JAR}", "newickRenamer.NewickRenamer"] + args
|
|
668
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="newickRename")
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def _hmmerSearch_impl(args, verbose=False, quiet=False):
|
|
672
|
+
"""hmmerSearch: hmmerSearch <target.fa> <hmmDb> <out.tsv> # Advanced HMMer 全库扫描+domtblout 解析(插件 P00680 CLI 化,无需 idList)"""
|
|
673
|
+
pjar = os.path.join(ROOT, "plugins", "lib", "Plugin_HmmerSuite.jar")
|
|
674
|
+
if not os.path.isfile(pjar):
|
|
675
|
+
print(f"❌ 插件缺失: {pjar}", file=sys.stderr)
|
|
676
|
+
return 1
|
|
677
|
+
ensure_bridge("HmmerSuiteCli")
|
|
678
|
+
java_args = ["java", "-Xmx2g", "-cp", cp(BUILD_DIR, pjar, JAR), "HmmerSuiteCli"] + args
|
|
679
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="hmmerSearch")
|
|
680
|
+
|
|
681
|
+
|
|
682
|
+
def _memeViz_impl(args, verbose=False, quiet=False):
|
|
683
|
+
"""memeViz: memeViz <meme.xml> <out.svg> [width] [height] # MEME motif 批量可视化(插件 P00700 CLI 化,每 motif 一面板)"""
|
|
684
|
+
pjar = os.path.join(ROOT, "plugins", "lib", "Batch_MEME_Motif_Viz.jar")
|
|
685
|
+
if not os.path.isfile(pjar):
|
|
686
|
+
print(f"❌ 插件缺失: {pjar}", file=sys.stderr)
|
|
687
|
+
return 1
|
|
688
|
+
ensure_bridge("BatchVizMotifsCli")
|
|
689
|
+
java_args = ["java", "-Xmx2g", "-cp", cp(BUILD_DIR, pjar, JAR), "BatchVizMotifsCli"] + args
|
|
690
|
+
return run_plot(java_args, verbose=verbose, quiet=quiet, command_name="memeViz")
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
def _gsea_impl(args, verbose=False, quiet=False):
|
|
694
|
+
"""gsea: gsea <go.obo> <query2go.tsv> <rank.rnk> <outDir> # GO 预排序 GSEA(插件 P00342 CLI 化,GSEAPreranked 全套报告;⚠️ set_min=15 小基因集会被过滤)"""
|
|
695
|
+
pjar = os.path.join(ROOT, "plugins", "lib", "Plugin_GSEAWrapper.jar")
|
|
696
|
+
dep = os.path.join(ROOT, "plugins", "lib", "Dependency")
|
|
697
|
+
if not (os.path.isfile(pjar) and os.path.isdir(dep)):
|
|
698
|
+
print(f"❌ GSEA 插件或 Dependency 缺失: {pjar}", file=sys.stderr)
|
|
699
|
+
return 1
|
|
700
|
+
ensure_bridge("GSEAWrapperCli")
|
|
701
|
+
java_args = ["java", "-Xmx4g", "-cp", cp(BUILD_DIR, pjar, JAR), "GSEAWrapperCli"] + args
|
|
702
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="gsea")
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def _tfbsShift_impl(args, verbose=False, quiet=False):
|
|
706
|
+
"""tfbsShift: tfbsShift <query.pep> <outPrefix> [threads] # 植物 TF 结合 motif 偏移分析(插件 P00551 CLI 化,参考数据内置 ath.pep+binding.motifs)"""
|
|
707
|
+
pjar = os.path.join(ROOT, "plugins", "lib", "Plugin_PlantTFbindingMotifShift.jar")
|
|
708
|
+
ath = os.path.join(ROOT, "plugins", "lib", "plantTF", "ath.pep")
|
|
709
|
+
motifs = os.path.join(ROOT, "plugins", "lib", "plantTF", "binding.motifs")
|
|
710
|
+
if not (os.path.isfile(pjar) and os.path.isfile(ath) and os.path.isfile(motifs)):
|
|
711
|
+
print(f"❌ TFBS 插件或参考数据缺失: {pjar}", file=sys.stderr)
|
|
712
|
+
return 1
|
|
713
|
+
ensure_bridge("MotifShiftCli")
|
|
714
|
+
java_args = ["java", "-Xmx3g", "-cp", cp(BUILD_DIR, pjar, JAR), "MotifShiftCli", ath, motifs] + args
|
|
715
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="tfbsShift")
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def _mcscanxd_impl(args, verbose=False, quiet=False):
|
|
719
|
+
"""mcscanxd: mcscanxd <wkDir> <genome1.fa> <genome2.fa> <gxf1> <gxf2> [threads] [blastHits] [evalue] # OneStep MCScanX-SuperFast(插件 P00370 CLI 化,diamond 加速,二进制随包)"""
|
|
720
|
+
pjar = os.path.join(ROOT, "plugins", "lib", "Plugin_OneStepMCScanX_Diamond.jar")
|
|
721
|
+
bin_dir = os.path.join(ROOT, "plugins", "lib", "bin")
|
|
722
|
+
if not os.path.isfile(pjar):
|
|
723
|
+
print(f"❌ 插件缺失: {pjar}", file=sys.stderr)
|
|
724
|
+
return 1
|
|
725
|
+
if not os.path.isfile(os.path.join(bin_dir, "diamond")):
|
|
726
|
+
print(f"❌ diamond 二进制缺失: {bin_dir}/diamond", file=sys.stderr)
|
|
727
|
+
return 1
|
|
728
|
+
os.environ["PATH"] = bin_dir + os.pathsep + os.environ.get("PATH", "")
|
|
729
|
+
ensure_bridge("MCScanXFastCli")
|
|
730
|
+
java_args = ["java", "-Xmx4g", "-cp", cp(BUILD_DIR, pjar, JAR), "MCScanXFastCli"] + args
|
|
731
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="mcscanxd")
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def _quickAnno_impl(args, verbose=False, quiet=False):
|
|
735
|
+
"""quickAnno: quickAnno <query.pep> <swissprotDb.fa> <out.txt> [threads] [maxHits] # diamond 蛋白快速注释(插件 P00480 CLI 化;⚠️ db 需带描述行,否则 Top 词频为空报错)"""
|
|
736
|
+
pjar = os.path.join(ROOT, "plugins", "lib", "Plugin_QuickProteinAnno.jar")
|
|
737
|
+
bin_dir = os.path.join(ROOT, "plugins", "lib", "bin")
|
|
738
|
+
if not os.path.isfile(pjar):
|
|
739
|
+
print(f"❌ 插件缺失: {pjar}", file=sys.stderr)
|
|
740
|
+
return 1
|
|
741
|
+
if not os.path.isfile(os.path.join(bin_dir, "diamond")):
|
|
742
|
+
print(f"❌ diamond 二进制缺失: {bin_dir}/diamond", file=sys.stderr)
|
|
743
|
+
return 1
|
|
744
|
+
os.environ["PATH"] = bin_dir + os.pathsep + os.environ.get("PATH", "")
|
|
745
|
+
ensure_bridge("QuickProteinAnnoCli")
|
|
746
|
+
java_args = ["java", "-Xmx3g", "-cp", cp(BUILD_DIR, pjar, JAR), "QuickProteinAnnoCli"] + args
|
|
747
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="quickAnno")
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def _smart_impl(args, verbose=False, quiet=False):
|
|
751
|
+
"""smart: smart <in.fa> <out.txt> # SMART 域注释(插件 P00060 CLI 化;⚠️ 联网 POST EMBL ismart.embl.de,约 10-60s,输出域位置+类型)"""
|
|
752
|
+
pjar = os.path.join(ROOT, "plugins", "lib", "Plugin_BatchSMART.jar")
|
|
753
|
+
if not os.path.isfile(pjar):
|
|
754
|
+
print(f"❌ 插件缺失: {pjar}", file=sys.stderr)
|
|
755
|
+
return 1
|
|
756
|
+
ensure_bridge("SubmitSMARTCli")
|
|
757
|
+
java_args = ["java", "-Xmx2g", "-cp", cp(BUILD_DIR, pjar, JAR), "SubmitSMARTCli"] + args
|
|
758
|
+
return run_java(java_args, verbose=verbose, quiet=quiet, command_name="smart")
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
# ── P0/P1 修复实现(N19/N23/N25:覆盖错误的注册表/表驱动路径,覆盖顺序=后定义者胜)──
|
|
762
|
+
|
|
763
|
+
def _findBestHomologyBatch_impl(args, verbose=False, quiet=False):
|
|
764
|
+
"""findBestHomologyBatch: findBestHomologyBatch <query.pep> <subject.pep> <outDir> [--targetIds ID[,ID2...]] [--threads N] [--plot false] [--sensitive 5,10]
|
|
765
|
+
# 最优同源批量查找(FindBestHomologyBatch;N19 修复)
|
|
766
|
+
# ⚠️ 引擎真实参数: --inQueryProteinSet/--inSubjectProteinSet/--targetIdList(GeneName\tID1[,ID2])/--outDir;
|
|
767
|
+
# 旧写法 --queryFasta/--subjectFasta/--outTable 是错误参数名,引擎拒参仍返 ec=0(静默成功+输出不创建)。
|
|
768
|
+
# ⚠️ 引擎不自动创建 --outDir;targetIdList 必需,缺省时自动取 query 全部 ID(targetName=All)。
|
|
769
|
+
# 产物: <outDir>/<targetName>.s<subjectId>.ids(最佳同源 ID 对,逗号分隔)。"""
|
|
770
|
+
pos, kw = [], {}
|
|
771
|
+
i = 0
|
|
772
|
+
while i < len(args):
|
|
773
|
+
a = args[i]
|
|
774
|
+
if a.startswith("--") and i + 1 < len(args):
|
|
775
|
+
kw[a[2:]] = args[i + 1]
|
|
776
|
+
i += 2
|
|
777
|
+
else:
|
|
778
|
+
pos.append(a)
|
|
779
|
+
i += 1
|
|
780
|
+
query = kw.get("inQueryProteinSet") or kw.get("queryFasta") or (pos[0] if pos else None)
|
|
781
|
+
subject = kw.get("inSubjectProteinSet") or kw.get("subjectFasta") or (pos[1] if len(pos) > 1 else None)
|
|
782
|
+
out_dir = kw.get("outDir") or kw.get("outTable") or (pos[2] if len(pos) > 2 else None)
|
|
783
|
+
if not query or not subject or not out_dir:
|
|
784
|
+
print("用法: findBestHomologyBatch <query.pep> <subject.pep> <outDir> [--targetIds ID,...] [--threads N] [--plot false]", file=sys.stderr)
|
|
785
|
+
return 1
|
|
786
|
+
for f in (query, subject):
|
|
787
|
+
if not os.path.isfile(f):
|
|
788
|
+
print(f"❌ 输入文件不存在: {f}", file=sys.stderr)
|
|
789
|
+
return 2
|
|
790
|
+
try:
|
|
791
|
+
os.makedirs(out_dir, exist_ok=True) # 引擎不自动创建
|
|
792
|
+
except OSError as e:
|
|
793
|
+
print(f"❌ 无法创建 outDir: {out_dir}: {e}", file=sys.stderr)
|
|
794
|
+
return 2
|
|
795
|
+
target_list = kw.get("targetIdList") or kw.get("targetIds")
|
|
796
|
+
tmp_targets = None
|
|
797
|
+
if not (target_list and os.path.isfile(target_list)):
|
|
798
|
+
ids = [l[1:].split()[0] for l in open(query, encoding="utf-8", errors="replace") if l.startswith(">")]
|
|
799
|
+
if not ids:
|
|
800
|
+
print("❌ query FASTA 无序列头", file=sys.stderr)
|
|
801
|
+
return 3
|
|
802
|
+
tmp_targets = safe_temp(prefix="tb_fbh.", suffix=".tsv")
|
|
803
|
+
with open(tmp_targets, "w") as fh:
|
|
804
|
+
fh.write(f"{kw.get('targetName') or 'All'}\t{','.join(ids)}\n")
|
|
805
|
+
target_list = tmp_targets
|
|
806
|
+
try:
|
|
807
|
+
jargs = ["java", "-Xmx4g", "-cp", JAR,
|
|
808
|
+
"biocjava.bioIO.BioSoftPipeServer.FindBestHomologyBatch",
|
|
809
|
+
"--inQueryProteinSet", query, "--inSubjectProteinSet", subject,
|
|
810
|
+
"--targetIdList", target_list, "--outDir", out_dir,
|
|
811
|
+
"--useDiamond", str(kw.get("useDiamond", "false")).lower(),
|
|
812
|
+
"--threads", str(kw.get("threads", 2)),
|
|
813
|
+
"--plot", str(kw.get("plot", "false")).lower()]
|
|
814
|
+
for opt in ("sensitive", "similarity", "weightCov", "extendClade"):
|
|
815
|
+
if opt in kw:
|
|
816
|
+
jargs += [f"--{opt}", kw[opt]]
|
|
817
|
+
ec = run_java(jargs, verbose=verbose, quiet=quiet, command_name="findBestHomologyBatch")
|
|
818
|
+
# 防引擎拒参仍 ec=0(N19 静默成功): 校验产物
|
|
819
|
+
if ec == 0:
|
|
820
|
+
outs = [f for f in os.listdir(out_dir) if f.endswith(".ids")] if os.path.isdir(out_dir) else []
|
|
821
|
+
if not outs:
|
|
822
|
+
print(f"❌ 引擎未产出结果(检查 --targetIds 与输入格式),outDir={out_dir}", file=sys.stderr)
|
|
823
|
+
ec = 1
|
|
824
|
+
return ec
|
|
825
|
+
finally:
|
|
826
|
+
if tmp_targets:
|
|
827
|
+
try:
|
|
828
|
+
os.unlink(tmp_targets)
|
|
829
|
+
except Exception:
|
|
830
|
+
pass
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
def _gffCdsPhaseCorrector_impl(args, verbose=False, quiet=False):
|
|
834
|
+
"""gffCdsPhaseCorrector: gffCdsPhaseCorrector --inGff <in.gff3> --outGff <out.gff3> [--problemGff <p.gff3>] [--report <r.txt>]
|
|
835
|
+
# CDS phase 校正(GffCdsPhaseCorrector;N25 修复:引擎为位置参数式 <in> <correct> <problematic> <report>,
|
|
836
|
+
# 原注册表直通导致 --inGff/--outGff 被当字面量文件、警告覆盖输入——现显式转位置参数并避免覆盖已存在输出)。"""
|
|
837
|
+
pos, kw = [], {}
|
|
838
|
+
i = 0
|
|
839
|
+
while i < len(args):
|
|
840
|
+
a = args[i]
|
|
841
|
+
if a.startswith("--") and i + 1 < len(args):
|
|
842
|
+
kw[a[2:]] = args[i + 1]
|
|
843
|
+
i += 2
|
|
844
|
+
else:
|
|
845
|
+
pos.append(a)
|
|
846
|
+
i += 1
|
|
847
|
+
in_gff = kw.get("inGff") or (pos[0] if pos else None)
|
|
848
|
+
out_gff = kw.get("outGff") or (pos[1] if len(pos) > 1 else None)
|
|
849
|
+
if not in_gff or not out_gff:
|
|
850
|
+
print("用法: gffCdsPhaseCorrector --inGff <in.gff3> --outGff <out.gff3>", file=sys.stderr)
|
|
851
|
+
return 1
|
|
852
|
+
if not os.path.isfile(in_gff):
|
|
853
|
+
print(f"❌ 输入文件不存在: {in_gff}", file=sys.stderr)
|
|
854
|
+
return 2
|
|
855
|
+
if os.path.abspath(in_gff) == os.path.abspath(out_gff):
|
|
856
|
+
print("❌ inGff 与 outGff 不能相同(引擎会覆盖输入,N25)", file=sys.stderr)
|
|
857
|
+
return 2
|
|
858
|
+
prob = kw.get("problemGff") or (out_gff + ".problem.gff3")
|
|
859
|
+
rep = kw.get("report") or (out_gff + ".report.txt")
|
|
860
|
+
od = os.path.dirname(os.path.abspath(out_gff))
|
|
861
|
+
if od:
|
|
862
|
+
os.makedirs(od, exist_ok=True)
|
|
863
|
+
jargs = ["java", "-Xmx2g", "-cp", JAR,
|
|
864
|
+
"biocjava.bioDoer.GXFUtils.GffCdsPhase.GffCdsPhaseCorrector",
|
|
865
|
+
in_gff, out_gff, prob, rep]
|
|
866
|
+
return run_java(jargs, verbose=verbose, quiet=quiet, command_name="gffCdsPhaseCorrector")
|
|
867
|
+
|
|
868
|
+
|
|
869
|
+
def _mirnatarget_impl(args, verbose=False, quiet=False):
|
|
870
|
+
"""mirnatarget: mirnatarget <mirna.fa> <target.fa> <out.tsv> [--evalue X]
|
|
871
|
+
# miRNA 靶标预测完整管线(N23 修复):ssearch36 -w 100 -W 25 -E X -m 10 -T 1 -i -U <mirna> <target> → TargetScoreCli
|
|
872
|
+
# 原表驱动误把本命令直通 TargetScoreCli(其输入是 ssearch36 的 m10,不是 FASTA),
|
|
873
|
+
# 导致第 2 参数被当输出清零、第 3 参被忽略、输出永不落盘——现恢复完整管线并校验输出。
|
|
874
|
+
# 依赖: ssearch36(fasta36 套件)。产物: miRNA\ttarget\tstrand\tbeg\tend\tscore\t..."""
|
|
875
|
+
pos, evalue, extra = [], 1.0, []
|
|
876
|
+
i = 0
|
|
877
|
+
while i < len(args):
|
|
878
|
+
a = args[i]
|
|
879
|
+
if a == "--evalue" and i + 1 < len(args):
|
|
880
|
+
try:
|
|
881
|
+
evalue = float(args[i + 1])
|
|
882
|
+
except ValueError:
|
|
883
|
+
pass
|
|
884
|
+
i += 2
|
|
885
|
+
elif a.startswith("--") and i + 1 < len(args):
|
|
886
|
+
extra += [a, args[i + 1]]
|
|
887
|
+
i += 2
|
|
888
|
+
else:
|
|
889
|
+
pos.append(a)
|
|
890
|
+
i += 1
|
|
891
|
+
if len(pos) < 3:
|
|
892
|
+
print("用法: mirnatarget <mirna.fa> <target.fa> <out.tsv> [--evalue X]", file=sys.stderr)
|
|
893
|
+
return 1
|
|
894
|
+
mirna, target, out = pos[0], pos[1], pos[2]
|
|
895
|
+
for f in (mirna, target):
|
|
896
|
+
if not os.path.isfile(f):
|
|
897
|
+
print(f"❌ 输入文件不存在: {f}", file=sys.stderr)
|
|
898
|
+
return 2
|
|
899
|
+
if os.path.abspath(out) in (os.path.abspath(mirna), os.path.abspath(target)):
|
|
900
|
+
print("❌ 输出文件与输入同名会覆盖输入(N23),请换输出名", file=sys.stderr)
|
|
901
|
+
return 2
|
|
902
|
+
ssearch = shutil.which("ssearch36")
|
|
903
|
+
if not ssearch:
|
|
904
|
+
print("❌ 缺少依赖 ssearch36(fasta36 套件),请先安装", file=sys.stderr)
|
|
905
|
+
return 4
|
|
906
|
+
od = os.path.dirname(os.path.abspath(out))
|
|
907
|
+
if od:
|
|
908
|
+
os.makedirs(od, exist_ok=True)
|
|
909
|
+
tmp_m10 = safe_temp(prefix="tb_mirna.", suffix=".m10")
|
|
910
|
+
try:
|
|
911
|
+
with open(tmp_m10, "w") as fh:
|
|
912
|
+
r = subprocess.run([ssearch, "-w", "100", "-W", "25", "-E", str(evalue),
|
|
913
|
+
"-m", "10", "-T", "1", "-i", "-U", mirna, target],
|
|
914
|
+
stdout=fh, stderr=subprocess.PIPE)
|
|
915
|
+
if r.returncode != 0 or not os.path.isfile(tmp_m10) or os.path.getsize(tmp_m10) == 0:
|
|
916
|
+
print(f"❌ ssearch36 未产出比对(0 命中或序列格式不符;evalue={evalue})", file=sys.stderr)
|
|
917
|
+
return 3
|
|
918
|
+
ensure_bridge("TargetScoreCli")
|
|
919
|
+
jargs = ["java", "-Xmx2g", "-cp", cp(BUILD_DIR, JAR), "TargetScoreCli", tmp_m10, out] + extra
|
|
920
|
+
ec = run_java(jargs, verbose=verbose, quiet=quiet, command_name="mirnatarget")
|
|
921
|
+
if ec == 0 and not os.path.isfile(out):
|
|
922
|
+
print(f"❌ 输出文件未生成: {out}", file=sys.stderr)
|
|
923
|
+
ec = 1
|
|
924
|
+
return ec
|
|
925
|
+
finally:
|
|
926
|
+
try:
|
|
927
|
+
os.unlink(tmp_m10)
|
|
928
|
+
except Exception:
|
|
929
|
+
pass
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
def _msy_impl(args, verbose=False, quiet=False):
|
|
934
|
+
"""msy: msy <simplifiedGff.pos> <links.txt> <chrLayout.txt> <out> [w] [h]
|
|
935
|
+
# 多物种微共线性图 / microsynteny / collinearity plot(N26 修复:原表驱动把 msy 注册为裸 GenericCli 透传,用户参数被当
|
|
936
|
+
# engineClass 导致 ClassNotFoundException——现按 tbplot.sh 已验证调用方式显式拼参数)
|
|
937
|
+
# 格式: pos=Chr\\tGene\\tStart\\tEnd;links=GeneA\\tGeneB\\t[r,g,b];layout=Genome: chr1 chr2"""
|
|
938
|
+
if len(args) < 4:
|
|
939
|
+
print("用法: msy <simplifiedGff.pos> <links.txt> <chrLayout.txt> <out> [w] [h]", file=sys.stderr)
|
|
940
|
+
return 1
|
|
941
|
+
pos, links, layout, out = args[0], args[1], args[2], args[3]
|
|
942
|
+
w, h = "1000", "800"
|
|
943
|
+
if len(args) >= 5:
|
|
944
|
+
w = args[4]
|
|
945
|
+
if len(args) >= 6:
|
|
946
|
+
h = args[5]
|
|
947
|
+
for f in (pos, links, layout):
|
|
948
|
+
if not os.path.isfile(f):
|
|
949
|
+
print(f"❌ 输入文件不存在: {f}", file=sys.stderr)
|
|
950
|
+
return 2
|
|
951
|
+
ensure_bridge("GenericCli")
|
|
952
|
+
jargs = ["java", "-Xmx3g", "-cp", cp(BUILD_DIR, JAR), "GenericCli",
|
|
953
|
+
"biocjava.bioDoer.JIGplotToolkit.Synteny.MultipleSpeciesSyteny", "plot", out,
|
|
954
|
+
"--set", "inSimplifiedGff", pos, "--set", "genePairInfoFile", links,
|
|
955
|
+
"--set", "chrLayoutFile", layout, "--width", w, "--height", h]
|
|
956
|
+
return run_plot(jargs, verbose=verbose, quiet=quiet, command_name="msy")
|
|
957
|
+
|
|
958
|
+
|
|
959
|
+
def _getLongestCompleteORF_impl(args, verbose=False, quiet=False):
|
|
960
|
+
"""getLongestCompleteORF: getLongestCompleteORF --inFa <seq.fa> --outORFs <out.fa>
|
|
961
|
+
# 批量最长完整 ORF 预测(N24 修复:原注册到 JavaFX 无 main 的 biocjava.bioIO.ORF.ORF,
|
|
962
|
+
# 改映射到 GetLongestORF(longestorf 同引擎,ArgsParser --inFa/--outORFs 实测可用))"""
|
|
963
|
+
return _longestorf_impl(args, verbose=verbose, quiet=quiet) # type: ignore[name-defined] # noqa: F821
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def _efpHeat_impl(args, verbose=False, quiet=False):
|
|
967
|
+
"""efpHeat: efpHeat --inTGA <plant.tga> --inSample2CC <sample2cc.txt> --expMat <expMat.tsv> --geneId <ID> --outImg <out>
|
|
968
|
+
# eFP 组织表达热图(单矩阵,generateSuperHeatMap)——N27 附带修复:引擎是 --key value 式,
|
|
969
|
+
# 旧 docstring 写成位置参数导致拒参;支持位置参数 <tga> <sample2cc> <expmat> <geneId> <out> 自动转换
|
|
970
|
+
# ⚠️ 需 fake DatatypeConverter(build/,JDK9+ 无 javax.xml.bind,ensure_bridge 自动重建)"""
|
|
971
|
+
has_flag = any(a.startswith("--") for a in args)
|
|
972
|
+
if has_flag:
|
|
973
|
+
# 命名参数直通(TGA 参数校验交给引擎)
|
|
974
|
+
jargs = ["java", "-Xmx3g", "-cp", cp(BUILD_DIR, JAR),
|
|
975
|
+
"biocjava.bioDoer.SimpleEfpBrowser.generateSuperHeatMap"] + args
|
|
976
|
+
return run_plot(jargs, verbose=verbose, quiet=quiet, command_name="efpHeat")
|
|
977
|
+
pos = [a for a in args if not a.startswith("--")]
|
|
978
|
+
if len(pos) < 5:
|
|
979
|
+
print("用法: efpHeat --inTGA <plant.tga> --inSample2CC <s2cc.txt> --expMat <exp.tsv> --geneId <ID> --outImg <out>", file=sys.stderr)
|
|
980
|
+
return 1
|
|
981
|
+
tga, s2cc, exp, gid, out = pos[0], pos[1], pos[2], pos[3], pos[4]
|
|
982
|
+
for f in (tga, s2cc, exp):
|
|
983
|
+
if not os.path.isfile(f):
|
|
984
|
+
print(f"❌ 输入文件不存在: {f}", file=sys.stderr)
|
|
985
|
+
return 2
|
|
986
|
+
jargs = ["java", "-Xmx3g", "-cp", cp(BUILD_DIR, JAR),
|
|
987
|
+
"biocjava.bioDoer.SimpleEfpBrowser.generateSuperHeatMap",
|
|
988
|
+
"--inTGA", tga, "--inSample2CC", s2cc, "--expMat", exp,
|
|
989
|
+
"--geneId", gid, "--outImg", out]
|
|
990
|
+
return run_plot(jargs, verbose=verbose, quiet=quiet, command_name="efpHeat")
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
def _tableMerge_impl(args, verbose=False, quiet=False):
|
|
994
|
+
"""tableMerge: tableMerge --inFileArr \"f1,f2,...\" --inColIndexArr \"0,1,...\" --outTable <out> [--defaultNAvalue NA] [--appendMergedKey true|false] [--rmKeyColumns true|false]
|
|
995
|
+
# 按键合并多个表格(N3 修复:引擎为 ArgsParser 式 --inFileArr/--inColIndexArr/--outTable,
|
|
996
|
+
# 旧 docstring 位置参数写法 <outTable> <inFile1> [...] 与引擎完全对不上;本 impl 兼容位置参数自动转换)"""
|
|
997
|
+
pos, kw = [], {}
|
|
998
|
+
i = 0
|
|
999
|
+
while i < len(args):
|
|
1000
|
+
a = args[i]
|
|
1001
|
+
if a.startswith("--") and i + 1 < len(args):
|
|
1002
|
+
kw[a[2:]] = args[i + 1]
|
|
1003
|
+
i += 2
|
|
1004
|
+
else:
|
|
1005
|
+
pos.append(a)
|
|
1006
|
+
i += 1
|
|
1007
|
+
# 位置参数兼容: <outTable> <inFile1> [<inFile2>...] → --outTable + --inFileArr
|
|
1008
|
+
if "inFileArr" not in kw and len(pos) >= 2:
|
|
1009
|
+
kw["outTable"] = pos[0]
|
|
1010
|
+
kw["inFileArr"] = ",".join(pos[1:])
|
|
1011
|
+
kw.setdefault("inColIndexArr", ",".join("0" for _ in pos[1:]))
|
|
1012
|
+
if "inFileArr" not in kw or "outTable" not in kw:
|
|
1013
|
+
print("用法: tableMerge --inFileArr \"f1,f2,...\" --inColIndexArr \"0,1,...\" --outTable <out>", file=sys.stderr)
|
|
1014
|
+
print(" (旧位置参数写法 <outTable> <inFile1> [...] 已兼容自动转换)", file=sys.stderr)
|
|
1015
|
+
return 1
|
|
1016
|
+
jargs = ["java", "-Xmx3g", "-cp", cp(BUILD_DIR, JAR), "biocjava.bioDoer.Table.TableMerger",
|
|
1017
|
+
"--inFileArr", kw["inFileArr"],
|
|
1018
|
+
"--inColIndexArr", kw.get("inColIndexArr", "0"),
|
|
1019
|
+
"--outTable", kw["outTable"]]
|
|
1020
|
+
for opt in ("defaultNAvalue", "appendMergedKey", "rmKeyColumns", "appendOnly"):
|
|
1021
|
+
if opt in kw:
|
|
1022
|
+
jargs += [f"--{opt}", kw[opt]]
|
|
1023
|
+
return run_java(jargs, verbose=verbose, quiet=quiet, command_name="tableMerge")
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
def _parallelMD5Check_impl(args, verbose=False, quiet=False):
|
|
1027
|
+
"""parallelMD5Check: parallelMD5Check <md5_list.txt> [threads]
|
|
1028
|
+
# 并行校验 MD5 列表(N6 修复:位置参数式 <md5_list_file> [threads];
|
|
1029
|
+
# 每行 `md5sum 文件名`,与 md5sum -c 同格式;原 registry 直通在参数形态错误时 ec=3)"""
|
|
1030
|
+
if not args:
|
|
1031
|
+
print("用法: parallelMD5Check <md5_list.txt> [threads]", file=sys.stderr)
|
|
1032
|
+
print(" 每行: <md5sum> <文件名>(与 md5sum -c 同格式)", file=sys.stderr)
|
|
1033
|
+
return 1
|
|
1034
|
+
lst = args[0]
|
|
1035
|
+
if not os.path.isfile(lst):
|
|
1036
|
+
print(f"❌ MD5 列表文件不存在: {lst}", file=sys.stderr)
|
|
1037
|
+
return 2
|
|
1038
|
+
threads = "24"
|
|
1039
|
+
if len(args) >= 2:
|
|
1040
|
+
threads = args[1]
|
|
1041
|
+
if not threads.isdigit():
|
|
1042
|
+
print(f"❌ threads 须为数字: {threads}", file=sys.stderr)
|
|
1043
|
+
return 2
|
|
1044
|
+
jargs = ["java", "-Xmx2g", "-cp", JAR, "biocjava.bioDoer.FileUtils.ParallelMD5Check", lst, threads]
|
|
1045
|
+
return run_java(jargs, verbose=verbose, quiet=quiet, command_name="parallelMD5Check")
|
|
1046
|
+
|
|
1047
|
+
|
|
1048
|
+
def _extractFeatureFromGTF_impl(args, verbose=False, quiet=False):
|
|
1049
|
+
"""extractFeatureFromGTF: extractFeatureFromGTF --inGtf <in.gtf> --inGenome <genome.fa> --outFile <out> [--targetFeature CDS|exon|...] [--targetIdTag transcript_id] [--retainAttr true|false]
|
|
1050
|
+
# GTF 特征提取(N6 修复:引擎 main 硬编码 Windows 默认路径,但 toolsKit.ArgsParser 可覆盖 → 走 ArgsParser 路线;
|
|
1051
|
+
# 旧注册表直通裸 main 必 FileNotFoundException;位置参数 <in.gtf> <genome.fa> <out> 兼容转换)
|
|
1052
|
+
# ⚠️ 输入 GTF 坐标须与 genome 匹配(越界会 NPE);输出 FASTA 头含 +/− 链/Location 注释"""
|
|
1053
|
+
pos, kw = [], {}
|
|
1054
|
+
i = 0
|
|
1055
|
+
while i < len(args):
|
|
1056
|
+
a = args[i]
|
|
1057
|
+
if a.startswith("--") and i + 1 < len(args):
|
|
1058
|
+
kw[a[2:]] = args[i + 1]
|
|
1059
|
+
i += 2
|
|
1060
|
+
else:
|
|
1061
|
+
pos.append(a)
|
|
1062
|
+
i += 1
|
|
1063
|
+
in_gtf = kw.get("inGtf") or (pos[0] if pos else None)
|
|
1064
|
+
genome = kw.get("inGenome") or (pos[1] if len(pos) > 1 else None)
|
|
1065
|
+
out = kw.get("outFile") or (pos[2] if len(pos) > 2 else None)
|
|
1066
|
+
if not in_gtf or not genome or not out:
|
|
1067
|
+
print("用法: extractFeatureFromGTF --inGtf <in.gtf> --inGenome <genome.fa> --outFile <out> [--targetFeature CDS] [--targetIdTag transcript_id] [--retainAttr true]", file=sys.stderr)
|
|
1068
|
+
return 1
|
|
1069
|
+
for f in (in_gtf, genome):
|
|
1070
|
+
if not os.path.isfile(f):
|
|
1071
|
+
print(f"❌ 输入文件不存在: {f}", file=sys.stderr)
|
|
1072
|
+
return 2
|
|
1073
|
+
jargs = ["java", "-Xmx3g", "-cp", cp(BUILD_DIR, JAR), "biocjava.bioIO.GTF.ExtractFeaturefromGTFandGenome",
|
|
1074
|
+
"--inGtf", in_gtf, "--inGenome", genome, "--outFile", out,
|
|
1075
|
+
"--targetFeature", kw.get("targetFeature", "exon"),
|
|
1076
|
+
"--targetIdTag", kw.get("targetIdTag", "transcript_id"),
|
|
1077
|
+
"--retainAttr", kw.get("retainAttr", "false")]
|
|
1078
|
+
for opt in ("onlyCheck", "maxFeatureCounts", "minFeatureCounts"):
|
|
1079
|
+
if opt in kw:
|
|
1080
|
+
jargs += [f"--{opt}", kw[opt]]
|
|
1081
|
+
return run_java(jargs, verbose=verbose, quiet=quiet, command_name="extractFeatureFromGTF")
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
def _gxfSplit_impl(args, verbose=False, quiet=False):
|
|
1085
|
+
"""gxfSplit: gxfSplit <in.gff3|gtf> <outPrefix> [--numOfFile N]
|
|
1086
|
+
# GXF 按记录数拆分(§8.B N13 家族命令面缺口修复:RPC GxfSplit.process,走 RPC 自动拉起)"""
|
|
1087
|
+
import json as _json
|
|
1088
|
+
import urllib.request as _ur
|
|
1089
|
+
pos, num = [], 8
|
|
1090
|
+
i = 0
|
|
1091
|
+
while i < len(args):
|
|
1092
|
+
a = args[i]
|
|
1093
|
+
if a == "--numOfFile" and i + 1 < len(args):
|
|
1094
|
+
num = int(args[i + 1]); i += 2
|
|
1095
|
+
else:
|
|
1096
|
+
pos.append(a); i += 1
|
|
1097
|
+
if len(pos) < 2:
|
|
1098
|
+
print("用法: gxfSplit <in.gff3|gtf> <outPrefix> [--numOfFile N]", file=sys.stderr)
|
|
1099
|
+
return 1
|
|
1100
|
+
inp, prefix = pos[0], pos[1]
|
|
1101
|
+
if not os.path.isfile(inp):
|
|
1102
|
+
print(f"❌ 输入文件不存在: {inp}", file=sys.stderr)
|
|
1103
|
+
return 2
|
|
1104
|
+
from tbtools_cli.core import run_java # noqa
|
|
1105
|
+
# 走 RPC(服务器按需拉起,见 rpc call --no-autostart 之外的自动拉起)
|
|
1106
|
+
from tbtools_cli.cli_rpc import _ensure_rpc # 批次B: rpc 段已拆出
|
|
1107
|
+
ok = _ensure_rpc(8765)
|
|
1108
|
+
if not ok:
|
|
1109
|
+
print("❌ RPC 服务器不可用,无法调用 GxfSplit", file=sys.stderr)
|
|
1110
|
+
return 1
|
|
1111
|
+
body = _json.dumps({"jsonrpc":"2.0","method":"GxfSplit.process",
|
|
1112
|
+
"params":{"inputPath": inp, "outputPrefix": prefix, "numOfFile": num}, "id":1}).encode()
|
|
1113
|
+
req = _ur.Request("http://127.0.0.1:8765/rpc", data=body, headers={"Content-Type":"application/json"})
|
|
1114
|
+
resp = _json.loads(_ur.urlopen(req, timeout=120).read())
|
|
1115
|
+
if resp.get("error"):
|
|
1116
|
+
print(f"❌ RPC 错误: {resp['error']}", file=sys.stderr)
|
|
1117
|
+
return 1
|
|
1118
|
+
r = resp.get("result", {})
|
|
1119
|
+
print(f"✅ 拆分完成 {num} 份 × 前缀 {prefix}({r.get('outputPrefix', '')})", file=sys.stderr)
|
|
1120
|
+
return 0
|
|
1121
|
+
|
|
1122
|
+
|
|
1123
|
+
def _gxfIdAppender_impl(args, verbose=False, quiet=False):
|
|
1124
|
+
"""gxfIdAppender: gxfIdAppender <in.gff3|gtf> <out.gff3> <prefix>
|
|
1125
|
+
# GXF ID/染色体名前缀追加(§8.B N13 家族命令面缺口修复:RPC GxfIdAppender.process)"""
|
|
1126
|
+
import json as _json
|
|
1127
|
+
import urllib.request as _ur
|
|
1128
|
+
pos = [a for a in args if not a.startswith("--")]
|
|
1129
|
+
if len(pos) < 3:
|
|
1130
|
+
print("用法: gxfIdAppender <in.gff3|gtf> <out.gff3> <prefix>", file=sys.stderr)
|
|
1131
|
+
return 1
|
|
1132
|
+
inp, out, prefix = pos[0], pos[1], pos[2]
|
|
1133
|
+
if not os.path.isfile(inp):
|
|
1134
|
+
print(f"❌ 输入文件不存在: {inp}", file=sys.stderr)
|
|
1135
|
+
return 2
|
|
1136
|
+
from tbtools_cli.cli_rpc import _ensure_rpc # 批次B: rpc 段已拆出
|
|
1137
|
+
ok = _ensure_rpc(8765)
|
|
1138
|
+
if not ok:
|
|
1139
|
+
print("❌ RPC 服务器不可用,无法调用 GxfIdAppender", file=sys.stderr)
|
|
1140
|
+
return 1
|
|
1141
|
+
body = _json.dumps({"jsonrpc":"2.0","method":"GxfIdAppender.process",
|
|
1142
|
+
"params":{"inputPath": inp, "outputPath": out, "prefix": prefix}, "id":1}).encode()
|
|
1143
|
+
req = _ur.Request("http://127.0.0.1:8765/rpc", data=body, headers={"Content-Type":"application/json"})
|
|
1144
|
+
resp = _json.loads(_ur.urlopen(req, timeout=120).read())
|
|
1145
|
+
if resp.get("error"):
|
|
1146
|
+
print(f"❌ RPC 错误: {resp['error']}", file=sys.stderr)
|
|
1147
|
+
return 1
|
|
1148
|
+
print(f"✅ ID 前缀追加完成: {out}", file=sys.stderr)
|
|
1149
|
+
return 0
|