diff --git a/rsconcept/backend/apps/oss/tests/s_propagation/t_attributes.py b/rsconcept/backend/apps/oss/tests/s_propagation/t_attributes.py index c0e81a09..d3c9ca5d 100644 --- a/rsconcept/backend/apps/oss/tests/s_propagation/t_attributes.py +++ b/rsconcept/backend/apps/oss/tests/s_propagation/t_attributes.py @@ -58,7 +58,6 @@ class TestChangeAttributes(EndpointTester): self.operation3.refresh_from_db() self.ks3 = RSForm(self.operation3.result) - @decl_endpoint('/api/library/{item}/set-owner', method='patch') def test_set_owner(self): data = {'user': self.user3.pk} diff --git a/rsconcept/backend/apps/oss/tests/s_propagation/t_constituents.py b/rsconcept/backend/apps/oss/tests/s_propagation/t_constituents.py index c8ccefce..ae160773 100644 --- a/rsconcept/backend/apps/oss/tests/s_propagation/t_constituents.py +++ b/rsconcept/backend/apps/oss/tests/s_propagation/t_constituents.py @@ -57,6 +57,33 @@ class TestChangeConstituents(EndpointTester): self.ks3 = RSForm(self.operation3.result) self.assertEqual(self.ks3.constituents().count(), 4) + @decl_endpoint('/api/rsforms/{item}/details', method='get') + def test_retrieve_inheritance(self): + response = self.executeOK(item=self.ks3.model.pk) + self.assertEqual(response.data['oss'], [{'id': self.owned.model.pk, 'alias': 'T1'}]) + self.assertEqual(response.data['inheritance'], [ + { + 'child': Constituenta.objects.get(as_child__parent_id=self.ks1X1.pk).pk, + 'child_source': self.ks3.model.pk, + 'parent': self.ks1X1.pk, 'parent_source': self.ks1.model.pk + }, + { + 'child': Constituenta.objects.get(as_child__parent_id=self.ks1X2.pk).pk, + 'child_source': self.ks3.model.pk, + 'parent': self.ks1X2.pk, 'parent_source': self.ks1.model.pk + }, + { + 'child': Constituenta.objects.get(as_child__parent_id=self.ks2X1.pk).pk, + 'child_source': self.ks3.model.pk, + 'parent': self.ks2X1.pk, 'parent_source': self.ks2.model.pk + }, + { + 'child': Constituenta.objects.get(as_child__parent_id=self.ks2D1.pk).pk, + 'child_source': self.ks3.model.pk, + 'parent': self.ks2D1.pk, 'parent_source': self.ks2.model.pk + }, + ]) + @decl_endpoint('/api/rsforms/{schema}/create-cst', method='post') def test_create_constituenta(self): data = { diff --git a/rsconcept/backend/apps/rsform/serializers/__init__.py b/rsconcept/backend/apps/rsform/serializers/__init__.py index 5916b6ae..9792f640 100644 --- a/rsconcept/backend/apps/rsform/serializers/__init__.py +++ b/rsconcept/backend/apps/rsform/serializers/__init__.py @@ -4,6 +4,7 @@ from .basics import ( ASTNodeSerializer, ExpressionParseSerializer, ExpressionSerializer, + InheritanceDataSerializer, MultiFormSerializer, ResolverSerializer, TextSerializer, diff --git a/rsconcept/backend/apps/rsform/serializers/basics.py b/rsconcept/backend/apps/rsform/serializers/basics.py index 40afd0a5..4dbe7e5b 100644 --- a/rsconcept/backend/apps/rsform/serializers/basics.py +++ b/rsconcept/backend/apps/rsform/serializers/basics.py @@ -122,6 +122,14 @@ class ReferenceSerializer(serializers.Serializer): pos_output = TextPositionSerializer() +class InheritanceDataSerializer(serializers.Serializer): + ''' Serializer: inheritance data. ''' + child = serializers.IntegerField() + child_source = serializers.IntegerField() + parent = serializers.IntegerField() + parent_source = serializers.IntegerField() + + class ResolverSerializer(serializers.Serializer): ''' Serializer: Resolver results serializer. ''' input = serializers.CharField() diff --git a/rsconcept/backend/apps/rsform/serializers/data_access.py b/rsconcept/backend/apps/rsform/serializers/data_access.py index 2a8593a3..4b1a8d57 100644 --- a/rsconcept/backend/apps/rsform/serializers/data_access.py +++ b/rsconcept/backend/apps/rsform/serializers/data_access.py @@ -17,7 +17,7 @@ from apps.oss.models import Inheritance from shared import messages as msg from ..models import Constituenta, CstType, RSForm -from .basics import CstParseSerializer +from .basics import CstParseSerializer, InheritanceDataSerializer from .io_pyconcept import PyConceptAdapter @@ -103,7 +103,7 @@ class RSFormSerializer(serializers.ModelSerializer): child=CstSerializer() ) inheritance = serializers.ListField( - child=serializers.ListField(child=serializers.IntegerField()) + child=InheritanceDataSerializer() ) oss = serializers.ListField( child=LibraryItemReferenceSerializer() @@ -116,8 +116,17 @@ class RSFormSerializer(serializers.ModelSerializer): def to_representation(self, instance: LibraryItem) -> dict: result = self.to_base_data(instance) - for link in Inheritance.objects.filter(Q(child__schema=instance) | Q(parent__schema=instance)): - result['inheritance'].append([link.child.pk, link.parent.pk]) + inheritances = Inheritance.objects \ + .filter(Q(child__schema=instance) | Q(parent__schema=instance)) \ + .select_related('parent__schema', 'child__schema') \ + .only('parent__id', 'parent__schema__id', 'child__id', 'child__schema__id') + for link in inheritances: + result['inheritance'].append({ + 'child': link.child_id, + 'child_source': link.child.schema_id, + 'parent': link.parent_id, + 'parent_source': link.parent.schema_id + }) return result def to_base_data(self, instance: LibraryItem) -> dict: diff --git a/rsconcept/frontend/src/components/info/InfoConstituenta.tsx b/rsconcept/frontend/src/components/info/InfoConstituenta.tsx index 2b4b8c83..23187ada 100644 --- a/rsconcept/frontend/src/components/info/InfoConstituenta.tsx +++ b/rsconcept/frontend/src/components/info/InfoConstituenta.tsx @@ -39,16 +39,16 @@ function InfoConstituenta({ data, className, ...restProps }: InfoConstituentaPro {data.definition_resolved}

) : null} - {data.parent_alias ? ( + {data.spawner_alias ? (

Основание: - {data.parent_alias} + {data.spawner_alias}

) : null} - {data.children_alias.length > 0 ? ( + {data.spawn_alias.length > 0 ? (

Порождает: - {data.children_alias.join(', ')} + {data.spawn_alias.join(', ')}

) : null} {data.convention ? ( diff --git a/rsconcept/frontend/src/models/RSFormLoader.ts b/rsconcept/frontend/src/models/RSFormLoader.ts index 1ad6b30c..6685a9e4 100644 --- a/rsconcept/frontend/src/models/RSFormLoader.ts +++ b/rsconcept/frontend/src/models/RSFormLoader.ts @@ -3,6 +3,7 @@ */ import { Graph } from './Graph'; +import { LibraryItemID } from './library'; import { ConstituentaID, CstType, IConstituenta, IRSForm, IRSFormData, IRSFormStats } from './rsform'; import { inferClass, inferStatus, inferTemplate, isBaseSet, isFunctional } from './rsformAPI'; import { ParsingStatus, ValueClass } from './rslang'; @@ -59,27 +60,34 @@ export class RSFormLoader { } private inferCstAttributes() { - const inherit_children = new Set(this.schema.inheritance.map(item => item[0])); - const inherit_parents = new Set(this.schema.inheritance.map(item => item[1])); + const parent_schemas = new Map(); + this.schema.inheritance.forEach(item => { + if (item.child_source === this.schema.id) { + parent_schemas.set(item.child, item.parent_source); + } + }); + const inherit_children = new Set(this.schema.inheritance.map(item => item.child)); + const inherit_parents = new Set(this.schema.inheritance.map(item => item.parent)); this.graph.topologicalOrder().forEach(cstID => { const cst = this.cstByID.get(cstID)!; cst.status = inferStatus(cst.parse.status, cst.parse.valueClass); cst.is_template = inferTemplate(cst.definition_formal); cst.cst_class = inferClass(cst.cst_type, cst.is_template); - cst.children = []; - cst.children_alias = []; + cst.spawn = []; + cst.spawn_alias = []; + cst.parent_schema = parent_schemas.get(cst.id); cst.is_inherited = inherit_children.has(cst.id); - cst.is_inherited_parent = inherit_parents.has(cst.id); + cst.has_inherited_children = inherit_parents.has(cst.id); cst.is_simple_expression = this.inferSimpleExpression(cst); if (!cst.is_simple_expression || cst.cst_type === CstType.STRUCTURED) { return; } - cst.parent = this.inferParent(cst); - if (cst.parent) { - const parent = this.cstByID.get(cst.parent)!; - cst.parent_alias = parent.alias; - parent.children.push(cst.id); - parent.children_alias.push(cst.alias); + cst.spawner = this.inferParent(cst); + if (cst.spawner) { + const parent = this.cstByID.get(cst.spawner)!; + cst.spawner_alias = parent.alias; + parent.spawn.push(cst.id); + parent.spawn_alias.push(cst.alias); } }); } @@ -107,7 +115,7 @@ export class RSFormLoader { if (sources.size !== 1 || sources.has(target.id)) { return undefined; } - const parent_id = sources.values().next().value as ConstituentaID; + const parent_id = sources.values().next().value!; const parent = this.cstByID.get(parent_id); if (parent && isBaseSet(parent.cst_type)) { return undefined; @@ -122,7 +130,7 @@ export class RSFormLoader { node.inputs.forEach(id => { const parent = this.cstByID.get(id)!; if (!parent.is_template || !parent.is_simple_expression) { - sources.add(parent.parent ?? id); + sources.add(parent.spawner ?? id); } }); return sources; @@ -133,7 +141,7 @@ export class RSFormLoader { bodyDependencies.forEach(alias => { const parent = this.cstByAlias.get(alias); if (parent && (!parent.is_template || !parent.is_simple_expression)) { - sources.add(this.cstByID.get(parent.id)!.parent ?? parent.id); + sources.add(this.cstByID.get(parent.id)!.spawner ?? parent.id); } }); const needCheckHead = () => { @@ -142,7 +150,7 @@ export class RSFormLoader { } else if (sources.size !== 1) { return false; } else { - const base = this.cstByID.get(sources.values().next().value as ConstituentaID)!; + const base = this.cstByID.get(sources.values().next().value!)!; return !isFunctional(base.cst_type) || splitTemplateDefinition(base.definition_formal).head !== expression.head; } }; @@ -151,7 +159,7 @@ export class RSFormLoader { headDependencies.forEach(alias => { const parent = this.cstByAlias.get(alias); if (parent && !isBaseSet(parent.cst_type) && (!parent.is_template || !parent.is_simple_expression)) { - sources.add(parent.parent ?? parent.id); + sources.add(parent.spawner ?? parent.id); } }); } diff --git a/rsconcept/frontend/src/models/miscellaneousAPI.ts b/rsconcept/frontend/src/models/miscellaneousAPI.ts index 27995dbd..6d077e90 100644 --- a/rsconcept/frontend/src/models/miscellaneousAPI.ts +++ b/rsconcept/frontend/src/models/miscellaneousAPI.ts @@ -44,6 +44,6 @@ export function applyNodeSizing(target: IConstituenta, sizing: GraphSizing): num } else if (sizing === 'complex') { return target.is_simple_expression ? 1 : 2; } else { - return target.parent ? 1 : 2; + return target.spawner ? 1 : 2; } } diff --git a/rsconcept/frontend/src/models/rsform.ts b/rsconcept/frontend/src/models/rsform.ts index 2b2c1584..9fdf612c 100644 --- a/rsconcept/frontend/src/models/rsform.ts +++ b/rsconcept/frontend/src/models/rsform.ts @@ -101,16 +101,31 @@ export interface IConstituentaData extends IConstituentaMeta { * Represents Constituenta. */ export interface IConstituenta extends IConstituentaData { + /** {@link CstClass} of this {@link IConstituenta}. */ cst_class: CstClass; + /** {@link ExpressionStatus} of this {@link IConstituenta}. */ status: ExpressionStatus; + + /** Indicates if this {@link IConstituenta} is a template. */ is_template: boolean; + /** Indicates if this {@link IConstituenta} has a simple expression. */ is_simple_expression: boolean; + + /** {@link LibraryItemID} that contains parent of this inherited {@link IConstituenta}. */ + parent_schema?: LibraryItemID; + /** Indicates if this {@link IConstituenta} is inherited. */ is_inherited: boolean; - is_inherited_parent: boolean; - parent?: ConstituentaID; - parent_alias?: string; - children: number[]; - children_alias: string[]; + /** Indicates if this {@link IConstituenta} has children that are inherited. */ + has_inherited_children: boolean; + + /** {@link IConstituenta} that spawned this one. */ + spawner?: ConstituentaID; + /** Alias of {@link IConstituenta} that spawned this one. */ + spawner_alias?: string; + /** List of {@link IConstituenta} that are spawned by this one. */ + spawn: number[]; + /** List of aliases of {@link IConstituenta} that are spawned by this one. */ + spawn_alias: string[]; } /** @@ -198,12 +213,22 @@ export interface IRSFormStats { count_theorem: number; } +/** + * Represents inheritance data for {@link IRSForm}. + */ +export interface IInheritanceData { + child: ConstituentaID; + child_source: LibraryItemID; + parent: ConstituentaID; + parent_source: LibraryItemID; +} + /** * Represents data for {@link IRSForm} provided by backend. */ export interface IRSFormData extends ILibraryItemVersioned { items: IConstituentaData[]; - inheritance: ConstituentaID[][]; + inheritance: IInheritanceData[]; oss: ILibraryItemReference[]; } diff --git a/rsconcept/frontend/src/models/rsformAPI.ts b/rsconcept/frontend/src/models/rsformAPI.ts index 0d05d075..96615d17 100644 --- a/rsconcept/frontend/src/models/rsformAPI.ts +++ b/rsconcept/frontend/src/models/rsformAPI.ts @@ -117,9 +117,9 @@ export function inferClass(type: CstType, isTemplate: boolean = false): CstClass export function createMockConstituenta(id: ConstituentaID, alias: string, comment: string): IConstituenta { return { id: id, - parent: id, - children: [], - children_alias: [], + spawner: id, + spawn: [], + spawn_alias: [], is_simple_expression: false, schema: -1, alias: alias, @@ -134,7 +134,7 @@ export function createMockConstituenta(id: ConstituentaID, alias: string, commen status: ExpressionStatus.INCORRECT, is_template: false, is_inherited: false, - is_inherited_parent: false, + has_inherited_children: false, cst_class: CstClass.DERIVED, parse: { status: ParsingStatus.INCORRECT, diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx index 469b56f4..5f5ea0fa 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorConstituenta/FormConstituenta.tsx @@ -249,7 +249,7 @@ function FormConstituenta({ icon={} /> - {state.is_inherited_parent && !state.is_inherited ? ( + {state.has_inherited_children && !state.is_inherited ? ( } titleHtml='Внимание!
Конституента имеет потомков
в операционной схеме синтеза' diff --git a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/useGraphFilter.ts b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/useGraphFilter.ts index 91870e2c..fc2462a5 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/useGraphFilter.ts +++ b/rsconcept/frontend/src/pages/RSFormPage/EditorTermGraph/useGraphFilter.ts @@ -45,7 +45,7 @@ function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, } if (!focusCst && params.foldDerived) { schema.items.forEach(cst => { - if (cst.parent) { + if (cst.spawner) { graph.foldNode(cst.id); } }); @@ -53,7 +53,7 @@ function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams, if (focusCst) { const includes: ConstituentaID[] = [ focusCst.id, - ...focusCst.children, + ...focusCst.spawn, ...(params.focusShowInputs ? schema.graph.expandInputs([focusCst.id]) : []), ...(params.focusShowOutputs ? schema.graph.expandOutputs([focusCst.id]) : []) ]; diff --git a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/TableSideConstituents.tsx b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/TableSideConstituents.tsx index 11780219..8a8ec30a 100644 --- a/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/TableSideConstituents.tsx +++ b/rsconcept/frontend/src/pages/RSFormPage/ViewConstituents/TableSideConstituents.tsx @@ -109,13 +109,13 @@ function TableSideConstituents({ } }, { - when: (cst: IConstituenta) => !!activeCst && cst.parent === activeCst?.id && cst.id !== activeCst?.id, + when: (cst: IConstituenta) => !!activeCst && cst.spawner === activeCst?.id && cst.id !== activeCst?.id, style: { backgroundColor: colors.bgOrange50 } }, { - when: (cst: IConstituenta) => activeCst?.id !== undefined && cst.children.includes(activeCst.id), + when: (cst: IConstituenta) => activeCst?.id !== undefined && cst.spawn.includes(activeCst.id), style: { backgroundColor: colors.bgGreen50 } diff --git a/rsconcept/frontend/src/utils/codemirror.ts b/rsconcept/frontend/src/utils/codemirror.ts index 88d180f3..054ffb6a 100644 --- a/rsconcept/frontend/src/utils/codemirror.ts +++ b/rsconcept/frontend/src/utils/codemirror.ts @@ -215,15 +215,15 @@ export function domTooltipConstituenta(cst?: IConstituenta, canClick?: boolean): dom.appendChild(convention); } - if (cst.parent_alias) { + if (cst.spawner_alias) { const derived = document.createElement('p'); - derived.innerHTML = `Основание: ${cst.parent_alias}`; + derived.innerHTML = `Основание: ${cst.spawner_alias}`; dom.appendChild(derived); } - if (cst.children_alias.length > 0) { + if (cst.spawn_alias.length > 0) { const children = document.createElement('p'); - children.innerHTML = `Порождает: ${cst.children_alias.join(', ')}`; + children.innerHTML = `Порождает: ${cst.spawn_alias.join(', ')}`; dom.appendChild(children); }