index.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. "use strict";
  2. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  3. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  4. return new (P || (P = Promise))(function (resolve, reject) {
  5. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  8. step((generator = generator.apply(thisArg, _arguments || [])).next());
  9. });
  10. };
  11. Object.defineProperty(exports, "__esModule", { value: true });
  12. exports.getBlastRepr = void 0;
  13. const child_process_1 = require("child_process");
  14. const async_exec = (prog, args, onData, onErr) => {
  15. return new Promise((resolve, reject) => {
  16. const child = (0, child_process_1.spawn)(prog, args, { shell: true });
  17. child.stdout.on('data', data => onData(data.toString().trim()));
  18. child.stderr.on('data', data => onErr(data.toString().trim()));
  19. child.on('error', err => reject(err));
  20. child.on('exit', code => resolve(code));
  21. });
  22. };
  23. const diversitySeq = (Seq) => {
  24. return Seq.split('').reduce((prev, _curr, id, array) => {
  25. if (id != 0 && array[id] !== array[id - 1]) {
  26. return prev + (1 / array.length);
  27. }
  28. else {
  29. return prev;
  30. }
  31. }, 0);
  32. };
  33. const annotateSeq = (seq, blastDB, maxBlast = 100, minDiversity = 0.1, blastnPath = 'blastn') => __awaiter(void 0, void 0, void 0, function* () {
  34. return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () {
  35. try {
  36. let results = '';
  37. let sequence = { sequence: seq };
  38. if (sequence.sequence) {
  39. if (diversitySeq(sequence.sequence) > minDiversity) {
  40. const sequenceStr = '\'>GG\\n' + sequence.sequence + '\'';
  41. yield async_exec('echo', [sequenceStr, '|',
  42. blastnPath, '-db', blastDB, '-query', '-', '-outfmt', '6', '-max_target_seqs', '100'], (m) => results += m, console.log);
  43. if (results !== '') {
  44. //https://www.metagenomics.wiki/tools/blast/blastn-output-format-6
  45. const keys = [/*'qseqid',*/ 'sseqid', 'pident', 'length', 'mismatch', 'gapopen', 'qstart', 'qend', 'sstart', 'send', 'evalue', 'bitscore'];
  46. results.split('\n')
  47. .map((it, index) => sequence.blastn = [
  48. ...(sequence.blastn || []),
  49. Object.assign({ index }, it.split('\t')
  50. .slice(1)
  51. .reduce((a, v, i) => (Object.assign(Object.assign({}, a), { [keys[i]]: isNaN(parseInt(v)) ? v : parseInt(v) })), {}))
  52. ].splice(0, maxBlast));
  53. if (sequence.blastn.length === 0) {
  54. throw 'Blastn results parsing failed';
  55. }
  56. }
  57. else {
  58. // console.log('WARNING NO BLASTN RESULT', ['echo', '-e', ,'\'' + sequenceStr + '\'', '|',
  59. // blastn, '-db', blastDB, '-query', '-', '-outfmt', '6', '-max_target_seqs', '100'].join(' '))
  60. throw 'No blastn hit';
  61. }
  62. }
  63. else {
  64. throw 'Sequence diversity < ' + minDiversity;
  65. }
  66. }
  67. else {
  68. throw 'No sequence';
  69. }
  70. resolve(sequence);
  71. }
  72. catch (error) {
  73. reject(error);
  74. }
  75. }));
  76. });
  77. const transpose = (matrix) => matrix.reduce(($, row) => row.map((_, i) => [...($[i] || []), row[i]]), []);
  78. const whichMax = (arr) => arr.flatMap((v, i) => v === Math.max(...arr) ? i : []);
  79. const getBlastRepr = (args) => __awaiter(void 0, void 0, void 0, function* () {
  80. const { sequence, dbs } = args;
  81. try {
  82. let all_blastn = [];
  83. for (const cdb of dbs) {
  84. let res = [];
  85. try {
  86. res = yield annotateSeq(sequence, cdb);
  87. if (res.blastn.length > 0)
  88. all_blastn = [...all_blastn, ...res.blastn].map((v, i) => { return Object.assign(Object.assign({}, v), { index: i + 1 }); });
  89. }
  90. catch (e) { }
  91. }
  92. const indiv_match = all_blastn.map((blastn) => {
  93. const { start, end } = blastn.qstart <= blastn.qend ? { start: blastn.qstart, end: blastn.qend } : { end: blastn.qstart, start: blastn.qend };
  94. return sequence.split('').map((_, i) => ((i + 1) >= start && (i + 1) <= end) ? '|' : '_').join('');
  95. });
  96. const bestRepr = transpose(indiv_match.map((v) => v.split(''))).map((v) => {
  97. const tmp = v.map((c, i) => {
  98. if (c === '|') {
  99. return all_blastn[i].length;
  100. }
  101. else {
  102. return 0;
  103. }
  104. });
  105. if (Math.max(...tmp) === 0) {
  106. return 0;
  107. }
  108. else {
  109. return whichMax(tmp)[0] + 1;
  110. }
  111. });
  112. let bestReprRed = [];
  113. let n = 0;
  114. let start = 0;
  115. bestRepr.reduce((p, c, i) => {
  116. if (p !== c) {
  117. const name = p === 0 ? 'unknown' : all_blastn.filter((v) => v.index === p)[0].sseqid + ":" + all_blastn.filter((v) => v.index === p)[0].sstart + '-' + all_blastn.filter((v) => v.index === p)[0].send;
  118. bestReprRed.push({ name, n, start, end: i });
  119. start = (i + 1);
  120. n = 0;
  121. }
  122. n++;
  123. if (i === (bestRepr.length - 1)) {
  124. const name = c === 0 ? 'unknown' : all_blastn.filter((v) => v.index === c)[0].sseqid + ":" + all_blastn.filter((v) => v.index === c)[0].sstart + '-' + all_blastn.filter((v) => v.index === c)[0].send;
  125. bestReprRed.push({ name, n, start, end: i + 1 });
  126. }
  127. return c;
  128. });
  129. const sup = [sequence, ...indiv_match, bestRepr.join('')];
  130. return { short: bestReprRed.flatMap((ee) => ee.name + "{" + ee.n + "}").join("<>"), all_blastn, sup, bestReprRed };
  131. }
  132. catch (error) {
  133. console.log(error);
  134. return 1;
  135. }
  136. });
  137. exports.getBlastRepr = getBlastRepr;
  138. /*(async()=>{
  139. const sequence = 'ATCTTCACCACGAACTGCTGCTTGCTCGCTTGCTCCTCAGTCCTAGCTTCATCAAACACTGGTTCCTGGAATCCTGTCTGCTGCTGTCTTCCTAGATTCACTGAATCTTCACCACGAACTGCTGCTTGCTCGCTTGCTCCTCAGTCCTAGCTTCATCAA'
  140. const dbs = ['/home/thomas/NGS/ref/RNA/human_rna.fna']
  141. console.log(await getBlastRepr({sequence, dbs}));
  142. })()*/