@datagrok/bio
Advanced tools
Comparing version 2.12.12 to 2.12.13
@@ -20,4 +20,4 @@ { | ||
"max-len": ["error", 120], | ||
"max-params": ["warn", 5], | ||
"max-lines-per-function": ["warn", 30], | ||
"max-params": ["warn", 7], | ||
"max-lines-per-function": ["warn", 100], | ||
"max-lines": ["warn", 300], | ||
@@ -24,0 +24,0 @@ "no-unused-vars": "off", |
# Bio changelog | ||
## 2.12.13 (2024-04-15) | ||
Bio: Fix cell renderer for scatter plot, add test | ||
## 2.12.12 (2024-04-15) | ||
@@ -4,0 +8,0 @@ |
@@ -1,9 +0,1 @@ | ||
/** | ||
* openchemlib - Manipulate molecules | ||
* @version v7.5.0 | ||
* @date 2022-08-09T09:45:52.047Z | ||
* @link https://github.com/cheminfo/openchemlib-js | ||
* @license BSD-3-Clause | ||
*/ | ||
/** @license URI.js v4.4.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */ |
@@ -1,9 +0,1 @@ | ||
/** | ||
* openchemlib - Manipulate molecules | ||
* @version v7.5.0 | ||
* @date 2022-08-09T09:45:52.047Z | ||
* @link https://github.com/cheminfo/openchemlib-js | ||
* @license BSD-3-Clause | ||
*/ | ||
/** @license URI.js v4.4.1 (c) 2011 Gary Court. License: http://github.com/garycourt/uri-js */ |
@@ -8,3 +8,3 @@ { | ||
}, | ||
"version": "2.12.12", | ||
"version": "2.12.13", | ||
"description": "Bioinformatics support (import/export of sequences, conversion, visualization, analysis). [See more](https://github.com/datagrok-ai/public/blob/master/packages/Bio/README.md) for details.", | ||
@@ -38,8 +38,8 @@ "repository": { | ||
"@biowasm/aioli": "^3.1.0", | ||
"@datagrok-libraries/bio": "^5.40.6", | ||
"@datagrok-libraries/chem-meta": "^1.2.4", | ||
"@datagrok-libraries/bio": "^5.40.8", | ||
"@datagrok-libraries/chem-meta": "^1.2.5", | ||
"@datagrok-libraries/math": "^1.1.1", | ||
"@datagrok-libraries/ml": "^6.6.0", | ||
"@datagrok-libraries/tutorials": "^1.3.12", | ||
"@datagrok-libraries/utils": "^4.2.0", | ||
"@datagrok-libraries/math": "^1.1.1", | ||
"@webgpu/types": "^0.1.40", | ||
@@ -108,3 +108,4 @@ "ajv": "^8.12.0", | ||
"sources": [ | ||
"css/helm.css" | ||
"css/helm.css", | ||
"common/openchemlib-full.js" | ||
], | ||
@@ -111,0 +112,0 @@ "category": "Bioinformatics", |
import * as grok from 'datagrok-api/grok'; | ||
import * as DG from 'datagrok-api/dg'; | ||
import * as ui from 'datagrok-api/ui'; | ||
import {category, expect, test, delay} from '@datagrok-libraries/utils/src/test'; | ||
import $ from 'cash-dom'; | ||
import {fromEvent} from 'rxjs'; | ||
import {category, expect, test, delay, testEvent} from '@datagrok-libraries/utils/src/test'; | ||
import {ALIGNMENT, ALPHABET, NOTATION, TAGS as bioTAGS} from '@datagrok-libraries/bio/src/utils/macromolecule'; | ||
@@ -15,2 +19,4 @@ import {SeqHandler} from '@datagrok-libraries/bio/src/utils/seq-handler'; | ||
import {_package} from '../package-test'; | ||
category('renderers', () => { | ||
@@ -52,2 +58,6 @@ test('long sequence performance ', async () => { | ||
test('scatterPlotTooltip', async () => { | ||
await _testScatterPlotTooltip(); | ||
}); | ||
async function _rendererMacromoleculeFasta() { | ||
@@ -202,2 +212,38 @@ const csv: string = await grok.dapi.files.readAsText('System:AppData/Bio/samples/FASTA.csv'); | ||
} | ||
const seqCoordsCsv = `seq,x,y | ||
ACGGTGTCGT,0,0 | ||
CGGTATCCCT,1,0 | ||
CTCGGCATGC,2,0 | ||
`; | ||
async function _testScatterPlotTooltip(): Promise<void> { | ||
const df = DG.DataFrame.fromCsv(seqCoordsCsv); | ||
df.currentRowIdx = 0; | ||
const view = grok.shell.addTableView(df); | ||
const sp: DG.ScatterPlotViewer = df.plot.scatter({x: 'x', y: 'y'}); | ||
view.dockManager.dock(sp, DG.DOCK_TYPE.RIGHT, null); | ||
await Promise.all([ | ||
testEvent(sp.onAfterDrawScene, () => {}, () => { sp.invalidateCanvas(); }, 1000), | ||
awaitGrid(view.grid, 500) | ||
]); | ||
const spBcr = sp.root.getBoundingClientRect(); | ||
const wp = sp.worldToScreen(1, 0); | ||
const ev = new MouseEvent('mousemove', { | ||
cancelable: true, bubbles: true, view: window, button: 0, | ||
clientX: spBcr.left + wp.x, clientY: spBcr.top + wp.y | ||
}); | ||
const spCanvas = $(sp.root).find('canvas').get()[0] as HTMLCanvasElement; | ||
await testEvent(fromEvent(spCanvas, 'mousemove'), () => { | ||
_package.logger.debug(`Test: event, currentRowIdx=${df.currentRowIdx}`); | ||
expect($(ui.tooltip.root).find('div table.d4-row-tooltip-table tr td canvas').length, 1); | ||
expect(sp.hitTest(wp.x, wp.y), 1); | ||
}, () => { | ||
spCanvas.dispatchEvent(ev); | ||
}, 500); | ||
// TODO: Any error occurred become 'Cannot read properties of null (reading 'get$columns')' because of scatter plot | ||
//await testEvent(sp.onAfterDrawScene, () => {}, () => { sp.invalidateCanvas(); }, 200); | ||
await awaitGrid(view.grid, 500); | ||
} | ||
}); |
@@ -62,2 +62,27 @@ import * as grok from 'datagrok-api/grok'; | ||
type RendererGridCellTemp = { | ||
[mmcrTemps.monomerPlacer]: MonomerPlacer | ||
} | ||
function getRendererFridCellTempTemp(gridCell: DG.GridCell): RendererGridCellTemp { | ||
/** Primarily store/get MonomerPlacer at GridColumn, fallback at (Table) Column for scatter plot tooltip */ | ||
let temp: RendererGridCellTemp | null = null; | ||
let gridCol: DG.GridColumn | null = null; | ||
try { gridCol = gridCell.gridColumn; } catch { gridCol = null; } | ||
temp = gridCol && gridCol.dart ? gridCol.temp as RendererGridCellTemp : null; | ||
if (!temp) { | ||
let tableCol: DG.Column | null = null; | ||
try { tableCol = gridCell.cell.column; } catch { tableCol = null; } | ||
if (!tableCol) { | ||
const k = 42; | ||
} | ||
temp = tableCol ? tableCol.temp as RendererGridCellTemp : null; | ||
} | ||
if (temp === null) | ||
throw new Error(`Monomer placer store (GridColumn or Column) not found.`); | ||
return temp; | ||
} | ||
export class MacromoleculeSequenceCellRenderer extends DG.GridCellRenderer { | ||
@@ -86,3 +111,3 @@ private padding: number = 5; | ||
//const tableColTemp: TempType = tableCol.temp; | ||
const seqColTemp: MonomerPlacer = gridCell.gridColumn.temp[mmcrTemps.monomerPlacer]; | ||
const seqColTemp: MonomerPlacer = getRendererFridCellTempTemp(gridCell)[mmcrTemps.monomerPlacer]; | ||
if (!seqColTemp) return; // Can do nothing without precalculated data | ||
@@ -170,3 +195,3 @@ | ||
let seqColTemp: MonomerPlacer = gridCell.gridColumn.temp[mmcrTemps.monomerPlacer]; | ||
let seqColTemp: MonomerPlacer = getRendererFridCellTempTemp(gridCell)[mmcrTemps.monomerPlacer]; | ||
if (!seqColTemp) { | ||
@@ -198,3 +223,3 @@ seqColTemp = new MonomerPlacer(grid, tableCol, | ||
// Store updated seqColTemp to the col temp | ||
if (seqColTemp.updated) gridCell.gridColumn.temp[mmcrTemps.monomerPlacer] = seqColTemp; | ||
if (seqColTemp.updated) getRendererFridCellTempTemp(gridCell)[mmcrTemps.monomerPlacer] = seqColTemp; | ||
@@ -201,0 +226,0 @@ g.save(); |
export const HELM_ITEM_SEPARATOR = '|'; | ||
export const HELM_SECTION_SEPARATOR = '$'; | ||
export const HYDROGEN_SYMBOL = 'H'; | ||
export const R_GROUP_ELEMENT_SYMBOL = 'R#'; | ||
export const enum V2K_CONST { | ||
MAX_ATOM_COUNT = 999, | ||
RGP_LINE_START = 'M RGP', | ||
ATOM_ALIAS_LINE_START = 'A ', | ||
} | ||
export const enum V3K_CONST { | ||
HEADER = ` | ||
RDKit 2D | ||
0 0 0 0 0 0 0 0 0 0999 V3000`, | ||
BEGIN_CTAB = 'M V30 BEGIN CTAB', | ||
COUNTS_LINE_START = 'M V30 COUNTS ', | ||
COUNTS_LINE_END = ' 0 0 0', | ||
BEGIN_ATOM_BLOCK = 'M V30 BEGIN ATOM', | ||
END_ATOM_BLOCK = 'M V30 END ATOM', | ||
BEGIN_BOND_BLOCK = 'M V30 BEGIN BOND', | ||
END_BOND_BLOCK = 'M V30 END BOND', | ||
BEGIN_COLLECTION_BLOCK = 'M V30 BEGIN COLLECTION', | ||
END_COLLECTION_BLOCK = 'M V30 END COLLECTION', | ||
END_CTAB = 'M V30 END CTAB', | ||
END = 'M END\n', | ||
} | ||
@@ -1,2 +0,2 @@ | ||
import {R_GROUP_ELEMENT_SYMBOL} from './const'; | ||
import {R_GROUP_ELEMENT_SYMBOL} from '@datagrok-libraries/chem-meta/src/formats/molfile-const'; | ||
@@ -3,0 +3,0 @@ export abstract class MolfileAtoms { |
import {RDModule} from '@datagrok-libraries/chem-meta/src/rdkit-api'; | ||
import {V2K_CONST, V3K_CONST} from './const'; | ||
import {V3K_CONST} from '@datagrok-libraries/chem-meta/src/formats/molfile-const'; | ||
import {Helm} from './helm'; | ||
@@ -47,3 +47,2 @@ import {MonomerWrapper} from './monomer-wrapper'; | ||
compileToMolfile(): string { | ||
const molfileHeader = '\nDatagrok\n'; | ||
const atomLines: string[] = []; | ||
@@ -77,4 +76,4 @@ const bondLines: string[] = []; | ||
private getV3KHeader(atomCount: number, bondCount: number): string { | ||
const countsLine = `${V3K_CONST.COUNTS_LINE_START}${atomCount} ${bondCount}${V3K_CONST.COUNTS_LINE_END}`; | ||
return `${V3K_CONST.HEADER}\n${V3K_CONST.BEGIN_CTAB}\n${countsLine}`; | ||
const countsLine = `${V3K_CONST.COUNTS_LINE_START}${atomCount} ${bondCount}${V3K_CONST.COUNTS_LINE_DUMMY_END}`; | ||
return `${V3K_CONST.DUMMY_HEADER}\n${V3K_CONST.BEGIN_CTAB}\n${countsLine}`; | ||
} | ||
@@ -81,0 +80,0 @@ |
import {HELM_REQUIRED_FIELD} from '@datagrok-libraries/bio/src/utils/const'; | ||
export const enum HELM_WRAPPER { | ||
LEFT = 'PEPTIDE1{', | ||
RIGHT = '}$$$$', | ||
} | ||
export const ALL_MONOMERS = '<All>'; | ||
@@ -8,0 +4,0 @@ |
@@ -9,3 +9,2 @@ import * as grok from 'datagrok-api/grok'; | ||
import {HELM_WRAPPER} from './const'; | ||
import {getMolColumnFromHelm} from '../helm-to-molfile/utils'; | ||
@@ -16,2 +15,6 @@ | ||
const enum HELM_WRAPPER { | ||
LEFT = 'PEPTIDE1{', | ||
RIGHT = '}$$$$', | ||
} | ||
@@ -48,15 +51,4 @@ type ConnectionData = { | ||
protected hasTerminals(helm: string): boolean { | ||
let isLinkable = false; | ||
if (helm.includes('(1)')) | ||
isLinkable = true; | ||
if (helm.includes('(2)')) | ||
isLinkable = true; | ||
return isLinkable; | ||
} | ||
protected getLinkedPositions(helm: string, rules: Rule[]): [number, number][] { | ||
const seq = helm.replace(HELM_WRAPPER.LEFT, '').replace(HELM_WRAPPER.RIGHT, ''); | ||
const monomers = seq.split('.').map((m) => { return m.replace('[', '').replace(']', ''); }); | ||
protected getLinkedPositions(monomers: string[], rules: Rule[]): [number, number][] { | ||
const monomersNames = monomers.map((m) => { return m.replace('[', '').replace(']', ''); }); | ||
const result: [number, number][] = new Array<[number, number]>(rules.length); | ||
@@ -71,10 +63,10 @@ | ||
const add = `(${rules[i].code})`; | ||
for (let j = 0; j < monomers.length; j++) { | ||
if (monomers[j].includes(add)) { | ||
for (let j = 0; j < monomersNames.length; j++) { | ||
if (monomersNames[j].includes(add)) { | ||
if (firstFound) { | ||
if (firstIsFirst && monomers[j] == rules[i].secondMonomer + add) { | ||
if (firstIsFirst && monomersNames[j] == rules[i].secondMonomer + add) { | ||
secondFound = true; | ||
secondEntryIndex = j; | ||
break; | ||
} else if (!firstIsFirst && monomers[j] == rules[i].firstMonomer + add) { | ||
} else if (!firstIsFirst && monomersNames[j] == rules[i].firstMonomer + add) { | ||
secondFound = true; | ||
@@ -90,7 +82,7 @@ secondEntryIndex = j; | ||
} else { | ||
if (monomers[j] == rules[i].firstMonomer + add) { | ||
if (monomersNames[j] == rules[i].firstMonomer + add) { | ||
firstFound = true; | ||
firstIsFirst = true; | ||
firstEntryIndex = j; | ||
} else if (monomers[j] == rules[i].secondMonomer + add) { | ||
} else if (monomersNames[j] == rules[i].secondMonomer + add) { | ||
firstFound = true; | ||
@@ -149,6 +141,28 @@ firstIsFirst = false; | ||
protected getTransformedHelm(helm: string, rules: Rule[]): string { | ||
const ruleCount = rules.length; | ||
const positions = this.getLinkedPositions(helm, rules); | ||
protected getDimeric(helm: string): [string[], [number, number][]] { | ||
const seq = helm.replace(HELM_WRAPPER.LEFT, '').replace(HELM_WRAPPER.RIGHT, ''); | ||
const monomers = seq.split('.'); | ||
const duplicates: [number, number][] = []; | ||
for (let i = 0; i < monomers.length; i++) { | ||
if (monomers[i].includes('(#2)')) { | ||
monomers[i] = monomers[i].replace('(#2)', ''); | ||
const duplicateStart = i + 1; | ||
let duplicateFinish = 0; | ||
for (let j = duplicateStart + 1; j < monomers.length; j++) { | ||
if (monomers[j].includes('}')) { | ||
duplicateFinish = j; break; | ||
} | ||
} | ||
monomers[duplicateStart] = monomers[duplicateStart].replace('{', ''); | ||
monomers[duplicateFinish] = monomers[duplicateFinish].replace('}', ''); | ||
duplicates.push([duplicateStart, duplicateFinish]); | ||
} | ||
} | ||
return [monomers, duplicates]; | ||
} | ||
protected getAllCycles(rules: Rule[], monomers: string [], positions: [number, number][]) : | ||
[string [], number [], number [], number [], number []] { | ||
const allPos1: number [] = []; | ||
@@ -158,2 +172,3 @@ const allPos2: number [] = []; | ||
const allAttaches2: number [] = []; | ||
const ruleCount = rules.length; | ||
@@ -164,5 +179,2 @@ for (let i = 0; i < ruleCount; i++) { | ||
//helm = helm.replaceAll(`(${i + 1})`, ''); | ||
const seq = helm.replace(HELM_WRAPPER.LEFT, '').replace(HELM_WRAPPER.RIGHT, ''); | ||
const monomers = seq.split('.'); | ||
const firstMonomer = monomers[positions[i][0]].replace('[', '').replace(']', ''); | ||
@@ -178,17 +190,87 @@ const secondMonomer = monomers[positions[i][1]].replace('[', '').replace(']', ''); | ||
allAttaches2.push(rules[i].secondR); | ||
} | ||
helm = HELM_WRAPPER.LEFT; | ||
for (let i = 0; i < monomers.length; i++) { | ||
if (i != monomers.length - 1) | ||
helm = helm + monomers[i] + '.'; | ||
else | ||
helm = helm + monomers[i]; | ||
return [monomers, allPos1, allPos2, allAttaches1, allAttaches2]; | ||
} | ||
protected getHelmCycle(source: ConnectionData, polFirst: number, poSecond: number): string { | ||
let cycled = ''; | ||
for (let i = 0; i < source.allPos1.length; i++) { | ||
if (i == 0) | ||
cycled += `PEPTIDE${polFirst},PEPTIDE${poSecond},`; | ||
else | ||
cycled += `|PEPTIDE${polFirst},PEPTIDE${poSecond},`; | ||
cycled += `${source.allPos1[i]}:R${source.allAttaches1[i]}-${source.allPos2[i]}:R${source.allAttaches2[i]}`; | ||
} | ||
return cycled; | ||
} | ||
//"PEPTIDE1{[(#2)Succ].[{R].F.[Dab(2)].T.G.H.F.G.A.A.Y.P.[E(2)].[NH2}]}$$$$" | ||
protected getTransformedHelm(helm: string, rules: Rule[]): string { | ||
const [monomers, duplicates] = this.getDimeric(helm); | ||
const positions = this.getLinkedPositions(monomers, rules); | ||
const [monomersCycled, allPos1, allPos2, allAttaches1, allAttaches2] = | ||
this.getAllCycles(rules, monomers, positions); | ||
helm = 'PEPTIDE1{'; | ||
for (let i = 0; i < monomersCycled.length; i++) | ||
helm += i != monomersCycled.length - 1 ? monomersCycled[i] + '.' : monomersCycled[i]; | ||
helm += '}'; | ||
const dimerCodes = new Array<string>(duplicates.length); | ||
const cycleCodes = new Array<string>(duplicates.length); | ||
for (let i = 0; i < duplicates.length; i++) { | ||
let helmAdd = `|PEPTIDE${i + 2}{`; | ||
const lengthAdd = duplicates[i][1] - duplicates[i][0]; | ||
//const monomersAdd = new Array<string>(lengthAdd); | ||
const allPosAdd1: number [] = []; | ||
const allPosAdd2: number [] = []; | ||
const allAttachesAdd1: number [] = []; | ||
const allAttachesAdd2: number [] = []; | ||
for (let j = 0; j <= lengthAdd; j ++) { | ||
const index = j + duplicates[i][0]; | ||
helmAdd += j != lengthAdd ? monomersCycled[index] + '.' : monomersCycled[index]; | ||
} | ||
helm = helm + HELM_WRAPPER.RIGHT; | ||
helmAdd += '}'; | ||
for (let j = 0; j < allPos1.length; j++) { | ||
if (allPos1[j] - 1 >= duplicates[i][0] && allPos1[j] - 1 <= duplicates[i][1]) { | ||
allPosAdd1.push(allPos1[j] - duplicates[i][0]); | ||
allPosAdd2.push(allPos2[j] - duplicates[i][0]); | ||
allAttachesAdd1.push(allAttaches1[j]); | ||
allAttachesAdd2.push(allAttaches1[j]); | ||
} | ||
} | ||
const addCyclysation = this.getHelmCycle({ | ||
allPos1: allPosAdd1, | ||
allPos2: allPosAdd2, | ||
allAttaches1: allAttachesAdd1, | ||
allAttaches2: allAttachesAdd2}, i + 2, i + 2); | ||
dimerCodes[i] = helmAdd; | ||
cycleCodes[i] = this.getHelmCycle({ | ||
allPos1: [duplicates[i][0]], | ||
allPos2: [1], | ||
allAttaches1: [1], | ||
allAttaches2: [1]}, 1, i + 2); | ||
cycleCodes.push(addCyclysation); | ||
} | ||
for (let i = 0; i < dimerCodes.length; i++) | ||
helm += dimerCodes[i]; | ||
const cycledHelm = getHelmCycle(helm, {allPos1, allPos2, allAttaches1, allAttaches2}); | ||
return cycledHelm; | ||
helm += '$'; | ||
const mainCyclysation = this.getHelmCycle({allPos1, allPos2, allAttaches1, allAttaches2}, 1, 1); | ||
helm += mainCyclysation; | ||
for (let i = 0; i < cycleCodes.length; i++) { | ||
helm += '|'; | ||
helm += cycleCodes[i]; | ||
} | ||
helm += '$$$'; | ||
return helm; | ||
} | ||
@@ -199,7 +281,3 @@ | ||
const resultList = this.helmColumn.toList().map((helm: string) => { | ||
if (this.hasTerminals(helm)) | ||
return this.getTransformedHelm(helm, rules); | ||
console.log(helm); | ||
return helm; | ||
return this.getTransformedHelm(helm, rules); | ||
}); | ||
@@ -218,17 +296,2 @@ return resultList; | ||
function getHelmCycle(helm: string, source: ConnectionData): string { | ||
let cycled = helm.replace(HELM_WRAPPER.RIGHT, '}$'); | ||
for (let i = 0; i < source.allPos1.length; i++) { | ||
if (i == 0) | ||
cycled += 'PEPTIDE1,PEPTIDE1,'; | ||
else | ||
cycled += '|PEPTIDE1,PEPTIDE1,'; | ||
cycled += `${source.allPos1[i]}:R${source.allAttaches1[i]}-${source.allPos2[i]}:R${source.allAttaches2[i]}`; | ||
} | ||
cycled += '$$$'; | ||
return cycled; | ||
} | ||
export async function addTransformedColumn( | ||
@@ -235,0 +298,0 @@ molColumn: DG.Column<string>, addHelm: boolean, ruleFiles: string[], chiralityEngine?: boolean |
@@ -5,2 +5,3 @@ const path = require('path'); | ||
const mode = 'development'; | ||
module.exports = { | ||
@@ -10,3 +11,3 @@ cache: { | ||
}, | ||
mode: 'development', | ||
mode: mode, | ||
entry: { | ||
@@ -34,3 +35,3 @@ package: ['./src/package.ts'], | ||
], | ||
devtool: 'source-map', | ||
devtool: mode === 'development' ? 'source-map' : 'inline-source-map', | ||
externals: { | ||
@@ -40,3 +41,3 @@ 'datagrok-api/dg': 'DG', | ||
'datagrok-api/ui': 'ui', | ||
'openchemlib/full.js': 'OCL', | ||
'openchemlib/full': 'OCL', | ||
'rxjs': 'rxjs', | ||
@@ -43,0 +44,0 @@ 'rxjs/operators': 'rxjs.operators', |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
21600689
251
39790
22