Skip to content

Commit

Permalink
#4388 - Macro: Allow Nucleoside of sequence edit in RNA builder
Browse files Browse the repository at this point in the history
  • Loading branch information
ilya-asiyuk-epam committed Apr 24, 2024
1 parent ca3b83f commit a71acc2
Show file tree
Hide file tree
Showing 58 changed files with 710 additions and 211 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,66 @@ test.describe('Sequence mode edit in RNA Builder', () => {
await takePageScreenshot(page);
});

test('Select nucleoside and phosphate and modify sugar and phosphate as it one nucleotide', async ({
page,
}) => {
// Coordinates for rectangle selection
const startX = 300;
const startY = 100;
const endX = 400;
const endY = 200;
await selectRectangleArea(page, startX, startY, endX, endY);
await clickOnSequenceSymbol(page, 'T', { button: 'right', nthNumber: 2 });
// should see correct context menu title and available 'modify_in_rna_builder' button
await takeEditorScreenshot(page);
await page.getByTestId('modify_in_rna_builder').click();
// should see uploaded nucleotide (nucleoside + phosphate) data to RNA Builder and disabled "Update" button
await takeRNABuilderScreenshot(page);
// Update Sugar
await page.getByTestId(SUGAR).click();
await page.getByTestId('25R___2,5-Ribose').click();
await moveMouseAway(page);
// Update Phosphate
await page.getByTestId(PHOSPHATE).click();
await page.getByTestId('bP___Boranophosphate').click();
await moveMouseAway(page);
// should see updated sugar and phosphate, updated title of preset and nondisabled "Update" button
await takeRNABuilderScreenshot(page);
await page.getByTestId('save-btn').click();
await takePageScreenshot(page);
});

test('Select nucleotide, nucleoside and modify sugar and phosphate. nucleoside should become nucleotide', async ({
page,
}) => {
// Coordinates for rectangle selection
const startX = 280;
const startY = 100;
const endX = 320;
const endY = 200;
await selectRectangleArea(page, startX, startY, endX, endY);
await clickOnSequenceSymbol(page, 'T', { button: 'right', nthNumber: 2 });
// should see correct context menu title and available 'modify_in_rna_builder' button
await takeEditorScreenshot(page);
await page.getByTestId('modify_in_rna_builder').click();
// should see uploaded data to RNA Builder and disabled "Update" button
await takeRNABuilderScreenshot(page);
// Update Sugar
await page.getByTestId(SUGAR).click();
await page.getByTestId('25R___2,5-Ribose').click();
await moveMouseAway(page);
// Update Phosphate
await page.getByTestId(PHOSPHATE).click();
await page.getByTestId('bP___Boranophosphate').click();
await moveMouseAway(page);
// should see updated sugar and phosphate of preset and nondisabled "Update" button
await takeRNABuilderScreenshot(page);
await page.getByTestId('save-btn').click();
// Click 'Yes' in modal
await page.getByText('Yes').click();
await takePageScreenshot(page);
});

test('Select one nucleotide and cancel modification', async ({ page }) => {
await clickOnSequenceSymbol(page, 'T');
await clickOnSequenceSymbol(page, 'T', { button: 'right' });
Expand Down Expand Up @@ -93,7 +153,7 @@ test.describe('Sequence mode edit in RNA Builder', () => {
await takeEditorScreenshot(page);
});

test('Select entire chain and see disabled modify_in_rna_builder button', async ({
test('Select entire chain and see enabled modify_in_rna_builder button', async ({
page,
}) => {
await page.keyboard.down('Control');
Expand All @@ -102,7 +162,7 @@ test.describe('Sequence mode edit in RNA Builder', () => {
// should see the whole chain selected
await takeEditorScreenshot(page);
await clickOnSequenceSymbol(page, 'T', { button: 'right' });
// should see correct context menu title and disabled 'modify_in_rna_builder' button
// should see correct context menu title and enabled 'modify_in_rna_builder' button
await takeEditorScreenshot(page);
});
});
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 6 additions & 2 deletions ketcher-autotests/tests/utils/macromolecules/sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import { Page } from '@playwright/test';
export async function clickOnSequenceSymbol(
page: Page,
symbolText: string,
clickOptions?: { button: 'right' | 'left' },
clickOptions?: { button?: 'right' | 'left'; nthNumber?: number },
) {
const symbolLocator = await getSequenceSymbolLocator(page, symbolText);
const symbolLocator = getSequenceSymbolLocator(
page,
symbolText,
clickOptions?.nthNumber,
);
await symbolLocator.click(clickOptions);
}

Expand Down
6 changes: 3 additions & 3 deletions packages/ketcher-core/src/application/editor/Editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SequenceType, Struct, Vec2 } from 'domain/entities';
import {
BaseTool,
IRnaPreset,
LabeledNucleotideWithPositionInSequence,
LabeledNodesWithPositionInSequence,
isBaseTool,
Tool,
ToolConstructorInterface,
Expand Down Expand Up @@ -197,7 +197,7 @@ export class CoreEditor {
this.onTurnOffSequenceEditInRNABuilderMode(),
);
this.events.modifySequenceInRnaBuilder.add(
(updatedSelection: LabeledNucleotideWithPositionInSequence[]) =>
(updatedSelection: LabeledNodesWithPositionInSequence[]) =>
this.onModifySequenceInRnaBuilder(updatedSelection),
);
this.events.changeSequenceTypeEnterMode.add((mode: SequenceType) =>
Expand Down Expand Up @@ -238,7 +238,7 @@ export class CoreEditor {
}

private onModifySequenceInRnaBuilder(
updatedSelection: LabeledNucleotideWithPositionInSequence[],
updatedSelection: LabeledNodesWithPositionInSequence[],
) {
if (!(this.mode instanceof SequenceMode)) {
return;
Expand Down
146 changes: 99 additions & 47 deletions packages/ketcher-core/src/application/editor/modes/SequenceMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {
NodesSelection,
SequenceRenderer,
} from 'application/render/renderers/sequence/SequenceRenderer';
import { AttachmentPointName, MonomerItemType } from 'domain/types';
import { AttachmentPointName } from 'domain/types';
import { Command } from 'domain/entities/Command';
import { BaseMonomer, SequenceType, Vec2 } from 'domain/entities';
import { BaseMonomer, Phosphate, SequenceType, Vec2 } from 'domain/entities';
import { BaseRenderer } from 'application/render/renderers/internal';
import { EmptySequenceNode } from 'domain/entities/EmptySequenceNode';
import { Nucleoside } from 'domain/entities/Nucleoside';
Expand All @@ -34,7 +34,7 @@ import { ChainsCollection } from 'domain/entities/monomer-chains/ChainsCollectio
import { DrawingEntitiesManager } from 'domain/entities/DrawingEntitiesManager';
import { Chain } from 'domain/entities/monomer-chains/Chain';
import { MonomerSequenceNode } from 'domain/entities/MonomerSequenceNode';
import { LabeledNucleotideWithPositionInSequence } from 'application/editor/tools/Tool';
import { LabeledNodesWithPositionInSequence } from 'application/editor/tools/Tool';

const naturalAnalogues = uniq([
...rnaDnaNaturalAnalogues,
Expand Down Expand Up @@ -154,53 +154,102 @@ export class SequenceMode extends BaseMode {
}

public modifySequenceInRnaBuilder(
updatedSelection: LabeledNucleotideWithPositionInSequence[],
updatedSelection: LabeledNodesWithPositionInSequence[],
) {
const editor = CoreEditor.provideEditorInstance();
const history = new EditorHistory(editor);
const modelChanges = new Command();

// Update Nucleotides one by one
for (const labeledNucleotide of updatedSelection) {
const nodeIndexOverall = labeledNucleotide.nodeIndexOverall;
for (const labeledNucleoelement of updatedSelection) {
const nodeIndexOverall = labeledNucleoelement.nodeIndexOverall;

if (nodeIndexOverall === undefined) return;

// Create monomerItem(s) based on label
const sugarMonomerItem = getRnaPartLibraryItem(
editor,
labeledNucleotide.sugarLabel,
);
const baseMonomerItem = getRnaPartLibraryItem(
editor,
labeledNucleotide.baseLabel,
);
const phosphateMonomerItem = getRnaPartLibraryItem(
editor,
labeledNucleotide.phosphateLabel,
);
let sugarMonomerItem;
let baseMonomerItem;
let phosphateMonomerItem;
if (labeledNucleoelement.sugarLabel) {
sugarMonomerItem = getRnaPartLibraryItem(
editor,
labeledNucleoelement.sugarLabel,
);
}
if (labeledNucleoelement.baseLabel) {
baseMonomerItem = getRnaPartLibraryItem(
editor,
labeledNucleoelement.baseLabel,
);
}
if (labeledNucleoelement.phosphateLabel) {
phosphateMonomerItem = getRnaPartLibraryItem(
editor,
labeledNucleoelement.phosphateLabel,
);
}

const currentNode = SequenceRenderer.getNodeByPointer(nodeIndexOverall);

// Update monomerItem objects
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.sugar,
sugarMonomerItem as MonomerItemType,
),
);
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.rnaBase,
baseMonomerItem as MonomerItemType,
),
);
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.phosphate,
phosphateMonomerItem as MonomerItemType,
),
);
// Update Sugar monomerItem object
if (currentNode.sugar && sugarMonomerItem) {
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.sugar,
sugarMonomerItem,
),
);
}
// Update Base monomerItem object
if (currentNode.rnaBase && baseMonomerItem) {
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.rnaBase,
baseMonomerItem,
),
);
}

// Update monomerItem object or add Phosphate
if (phosphateMonomerItem) {
// Update Phosphate monomerItem object for Nucleotide
if (currentNode instanceof Nucleotide) {
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.phosphate,
phosphateMonomerItem,
),
);
// Add Phosphate to Nucleoside
} else if (currentNode instanceof Nucleoside) {
const sugarR2 = currentNode.sugar.attachmentPointsToBonds.R2;
const nextMonomerInSameChain = sugarR2?.secondMonomer;

// Remove existing bond connection between Nucleoside Sugar and next node in case of any
if (sugarR2) {
modelChanges.merge(
editor.drawingEntitiesManager.deletePolymerBond(sugarR2),
);
}

modelChanges.merge(
this.bondNodesThroughNewPhosphate(
new Vec2(0, 0),
currentNode.sugar,
nextMonomerInSameChain,
labeledNucleoelement.phosphateLabel,
),
);
// Update Phosphate monomerItem object
} else if (currentNode.monomer instanceof Phosphate) {
modelChanges.merge(
editor.drawingEntitiesManager.modifyMonomerItem(
currentNode.monomer,
phosphateMonomerItem,
),
);
}
}
}

// Refresh UI
Expand Down Expand Up @@ -282,12 +331,13 @@ export class SequenceMode extends BaseMode {
private bondNodesThroughNewPhosphate(
position: Vec2,
previousMonomer: BaseMonomer,
nextMonomer: BaseMonomer,
nextMonomer?: BaseMonomer,
phosphate?: string,
) {
const editor = CoreEditor.provideEditorInstance();
const phosphateLibraryItem = getRnaPartLibraryItem(
editor,
RNA_DNA_NON_MODIFIED_PART.PHOSPHATE,
phosphate || RNA_DNA_NON_MODIFIED_PART.PHOSPHATE,
);

assert(phosphateLibraryItem);
Expand All @@ -309,14 +359,16 @@ export class SequenceMode extends BaseMode {
),
);

modelChanges.merge(
editor.drawingEntitiesManager.createPolymerBond(
additionalPhosphate,
nextMonomer,
AttachmentPointName.R2,
AttachmentPointName.R1,
),
);
if (nextMonomer) {
modelChanges.merge(
editor.drawingEntitiesManager.createPolymerBond(
additionalPhosphate,
nextMonomer,
AttachmentPointName.R2,
AttachmentPointName.R1,
),
);
}

return modelChanges;
}
Expand Down
13 changes: 8 additions & 5 deletions packages/ketcher-core/src/application/editor/tools/Tool.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MonomerItemType } from 'domain/types';
import { MonomerItemType, Entities } from 'domain/types';

interface ToolEventHandler {
click?(event: Event): void;
Expand Down Expand Up @@ -76,11 +76,14 @@ export interface IRnaPreset {
presetInList?: IRnaPreset;
}

export type LabeledNucleotideWithPositionInSequence = {
baseLabel: string;
sugarLabel: string;
phosphateLabel: string;
export type LabeledNodesWithPositionInSequence = {
type: Entities;
nodeIndexOverall: number;
baseLabel?: string;
sugarLabel?: string;
phosphateLabel?: string;
isNucleosideConnectedAndSelectedWithPhosphate?: boolean;
hasR1Connection?: boolean;
};

export interface Tool extends ToolEventHandler {
Expand Down
Loading

0 comments on commit a71acc2

Please sign in to comment.