Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/epam/ketcher into 2078-ce…
Browse files Browse the repository at this point in the history
…nter-molecules-after-layout-and-pasting
  • Loading branch information
Nitvex committed Jul 14, 2023
2 parents 9aec67f + e23a6c7 commit ad26c41
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 29 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
{ "functions": false, "typedefs": false }
],
"no-duplicate-imports": "error",
"no-alert": "error"
"no-alert": "error",
"comma-dangle": 0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -530,3 +530,10 @@ test('Open and Save file - Save V3000 molfile as V2000 molfile', async ({

expect(molFile).toEqual(molFileExpected);
});

test('Open V3000 file with R-Groups with Fragments', async ({ page }) => {
// Related Github issue https://github.com/epam/ketcher/issues/2774
await page.goto('');
await openFileAndAddToCanvas('RGroup-With-Fragments.mol', page);
await takeEditorScreenshot(page);
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 60 additions & 0 deletions ketcher-autotests/tests/test-data/RGroup-With-Fragments.mol
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

Marvin 02211216212D

0 0 0 0 0 999 V3000
M V30 BEGIN CTAB
M V30 COUNTS 8 7 0 0 0
M V30 BEGIN ATOM
M V30 1 C 7.6835 -7.3342 0 0
M V30 2 C 9.0171 -6.5642 0 0
M V30 3 C 10.3508 -7.3342 0 0
M V30 4 C 11.6845 -6.5642 0 0
M V30 5 C 13.0181 -7.3342 0 0
M V30 6 C 14.3518 -6.5642 0 0
M V30 7 C 15.6855 -7.3342 0 0
M V30 8 R# 17.0191 -6.5642 0 0 RGROUPS=(1 1)
M V30 END ATOM
M V30 BEGIN BOND
M V30 1 1 1 2
M V30 2 1 2 3
M V30 3 1 3 4
M V30 4 1 4 5
M V30 5 1 5 6
M V30 6 1 6 7
M V30 7 1 7 8
M V30 END BOND
M V30 END CTAB
M V30 BEGIN RGROUP 1
M V30 RLOGIC 0 0 >0
M V30 BEGIN CTAB
M V30 COUNTS 3 2 0 0 0
M V30 BEGIN ATOM
M V30 1 C 9.0283 -18.013 0 0 ATTCHPT=1
M V30 2 Br 10.362 -18.7831 0 0
M V30 3 O 9.0283 -16.473 0 0
M V30 END ATOM
M V30 BEGIN BOND
M V30 1 1 1 2
M V30 2 2 1 3
M V30 END BOND
M V30 END CTAB
M V30 BEGIN CTAB
M V30 COUNTS 6 5 0 0 0
M V30 BEGIN ATOM
M V30 1 C 13.7114 -17.3173 0 0 ATTCHPT=1
M V30 2 C 15.0451 -18.0874 0 0
M V30 3 F 16.1608 -16.9865 0 0
M V30 4 F 14.6601 -19.6326 0 0
M V30 5 F 16.3029 -18.8293 0 0
M V30 6 O 13.7114 -15.7773 0 0
M V30 END ATOM
M V30 BEGIN BOND
M V30 1 1 1 2
M V30 2 1 2 3
M V30 3 1 2 4
M V30 4 1 2 5
M V30 5 2 1 6
M V30 END BOND
M V30 END CTAB
M V30 END RGROUP
M END
4 changes: 2 additions & 2 deletions ketcher-autotests/tests/utils/files/readFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export async function openFile(filename: string, page: Page) {
export async function openFileAndAddToCanvas(filename: string, page: Page) {
await selectTopPanelButton(TopPanelButton.Open, page);
await openFile(filename, page);
await waitForLoad(page, () => {
pressButton(page, 'Add to Canvas');
await waitForLoad(page, async () => {
await pressButton(page, 'Add to Canvas');
});
await clickInTheMiddleOfTheScreen(page);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { Struct } from 'domain/entities';
import { KetItem } from 'domain/serializers/ket/fromKet/types';
import {
atomToStruct,
bondToStruct,
} from 'domain/serializers/ket/fromKet/moleculeToStruct';
import { mergeFragmentsToStruct } from 'domain/serializers/ket/fromKet/mergeFragmentsToStruct';

describe('mergeFragmentsToStruct', () => {
it('should return the same struct when ketItem is empty', () => {
const emptyKetItem: KetItem = {};
const struct: Struct = new Struct();
const result = mergeFragmentsToStruct(emptyKetItem, struct);

expect(result.atoms).toEqual(struct.atoms);
expect(result.bonds).toEqual(struct.bonds);
});

it('should merge single fragment into struct', () => {
const singleFragmentKetItem: KetItem = {
fragments: [
{
atoms: [
{
attachmentPoints: 1,
label: 'C',
location: [1, 2, 3],
},
],
},
],
};

const struct: Struct = new Struct();
const expectedStruct = new Struct();
expectedStruct.atoms.add(
atomToStruct({
attachmentPoints: 1,
label: 'C',
location: [1, 2, 3],
}),
);

const result = mergeFragmentsToStruct(singleFragmentKetItem, struct);
expect(result.atoms).toEqual(expectedStruct.atoms);
expect(result.bonds).toEqual(expectedStruct.bonds);
});

it('should merge multiple fragments with no bonds into struct', () => {
const multiFragmentKetItem: KetItem = {
fragments: [
{
atoms: [
{
attachmentPoints: 1,
label: 'C',
location: [1, 2, 3],
},
],
},
{
atoms: [
{
attachmentPoints: 1,
label: 'O',
location: [4, 5, 6],
},
],
},
],
};

const struct = new Struct();
const expectedStruct = new Struct();
expectedStruct.atoms.add(
atomToStruct({
attachmentPoints: 1,
label: 'C',
location: [1, 2, 3],
}),
);
expectedStruct.atoms.add(
atomToStruct({
attachmentPoints: 1,
label: 'O',
location: [4, 5, 6],
}),
);

const result = mergeFragmentsToStruct(multiFragmentKetItem, struct);
expect(result.atoms).toEqual(expectedStruct.atoms);
expect(result.bonds).toEqual(expectedStruct.bonds);
});

it('should merge multiple fragments with bonds into struct', () => {
const multiFragmentKetItem: KetItem = {
fragments: [
{
atoms: [
{
attachmentPoints: 1,
label: 'C',
location: [1, 2, 3],
},
{
attachmentPoints: 1,
label: 'O',
location: [4, 5, 6],
},
],
bonds: [
{
type: 1,
atoms: [0, 1],
},
],
},
{
atoms: [
{
attachmentPoints: 1,
label: 'N',
location: [7, 8, 9],
},
],
bonds: [],
},
],
};

const struct: Struct = new Struct();
const expectedStruct = new Struct();
const expectedAtoms = [
{
attachmentPoints: 1,
label: 'C',
location: [1, 2, 3],
},
{
attachmentPoints: 1,
label: 'O',
location: [4, 5, 6],
},
{
attachmentPoints: 1,
label: 'N',
location: [7, 8, 9],
},
];
expectedAtoms.forEach((atom) => {
expectedStruct.atoms.add(atomToStruct(atom));
});
expectedStruct.bonds.add(
bondToStruct({
type: 1,
atoms: [0, 1],
}),
);

const result = mergeFragmentsToStruct(multiFragmentKetItem, struct);
expect(result.atoms).toEqual(expectedStruct.atoms);
expect(result.bonds).toEqual(expectedStruct.bonds);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Struct } from 'domain/entities';
import { atomToStruct, bondToStruct } from './moleculeToStruct';
import { KetItem } from './types';

export function mergeFragmentsToStruct(
ketItem: KetItem,
struct: Struct,
): Struct {
let atomsOffset = 0;
if (ketItem.fragments) {
ketItem.fragments.forEach((fragment) => {
fragment.atoms?.forEach((atom) => struct.atoms.add(atomToStruct(atom)));
fragment.bonds?.forEach((bond) =>
struct.bonds.add(bondToStruct(bond, atomsOffset)),
);
atomsOffset += fragment.atoms?.length || 0;
});
}
return struct;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { Atom, Bond, SGroup, Struct } from 'domain/entities';

import { Elements } from 'domain/constants';
import { ifDef } from 'utilities';
import { mergeFragmentsToStruct } from './mergeFragmentsToStruct';

export function toRlabel(values) {
let res = 0;
Expand All @@ -29,12 +30,14 @@ export function toRlabel(values) {
}

export function moleculeToStruct(ketItem: any): Struct {
const struct = new Struct();
ketItem.atoms.forEach((atom) => {
if (atom.type === 'rg-label') struct.atoms.add(rglabelToStruct(atom));
if (atom.type === 'atom-list') struct.atoms.add(atomListToStruct(atom));
if (!atom.type) struct.atoms.add(atomToStruct(atom));
});
const struct = mergeFragmentsToStruct(ketItem, new Struct());
if (ketItem.atoms) {
ketItem.atoms.forEach((atom) => {
if (atom.type === 'rg-label') struct.atoms.add(rglabelToStruct(atom));
if (atom.type === 'atom-list') struct.atoms.add(atomListToStruct(atom));
if (!atom.type) struct.atoms.add(atomToStruct(atom));
});
}

if (ketItem.bonds) {
ketItem.bonds.forEach((bond) => struct.bonds.add(bondToStruct(bond)));
Expand Down Expand Up @@ -121,7 +124,22 @@ export function atomListToStruct(source) {
return new Atom(params);
}

export function bondToStruct(source) {
/**
*
* @param source
* @param atomOffset – if bond is a part of a fragment, then we need to consider atoms from previous fragment.
* source.atoms contains numbers related to fragment, but we need to count atoms related to struct. Example:
* fragments: [{
* atoms: [...],
* bonds: [...], this bonds point to atoms in the first fragment
* }, {
* atoms: [...],
* bonds: [...], this bonds point to atoms in the second fragment
* }]
* When we add bonds from second fragment we need to count atoms from fragments[0].atoms.length + 1, not from zero
* @returns newly created Bond
*/
export function bondToStruct(source, atomOffset = 0) {
const params: any = {};

ifDef(params, 'type', source.type);
Expand All @@ -132,8 +150,8 @@ export function bondToStruct(source) {
// if (params.stereo)
// params.stereo = params.stereo > 1 ? params.stereo * 2 : params.stereo;
// params.xxx = 0;
ifDef(params, 'begin', source.atoms[0]);
ifDef(params, 'end', source.atoms[1]);
ifDef(params, 'begin', source.atoms[0] + atomOffset);
ifDef(params, 'end', source.atoms[1] + atomOffset);

return new Bond(params);
}
Expand Down
22 changes: 22 additions & 0 deletions packages/ketcher-core/src/domain/serializers/ket/fromKet/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export interface KetAtom {
attachmentPoints: number;
label: string;
location: [number, number, number];
}

export interface KetBond {
type: number;
atoms: [number, number];
}

export interface KetFragment {
atoms?: KetAtom[];
bonds?: KetBond[];
}

export interface KetItem {
fragments?: KetFragment[];
rlogic?: {
number: number;
};
}
11 changes: 10 additions & 1 deletion packages/ketcher-core/src/domain/serializers/ket/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,18 @@
"default": 0
}
}
},
"fragments": {
"type": "array",
"items": {
"$ref": "#/definitions/structure"
}
}
},
"allOf": [
"anyOf": [
{
"required": ["fragments"]
},
{
"$ref": "#/definitions/structure"
}
Expand Down
Loading

0 comments on commit ad26c41

Please sign in to comment.