From 3555db3edf4cb21f65e24be747d6e81f100e4542 Mon Sep 17 00:00:00 2001
From: Ivan <8611739+IRBorisov@users.noreply.github.com>
Date: Sat, 14 Sep 2024 15:14:48 +0300
Subject: [PATCH] R: Add inherited cst source to API
---
.../oss/tests/s_propagation/t_attributes.py | 1 -
.../oss/tests/s_propagation/t_constituents.py | 27 +++++++++++++
.../apps/rsform/serializers/__init__.py | 1 +
.../backend/apps/rsform/serializers/basics.py | 8 ++++
.../apps/rsform/serializers/data_access.py | 17 ++++++--
.../src/components/info/InfoConstituenta.tsx | 8 ++--
rsconcept/frontend/src/models/RSFormLoader.ts | 40 +++++++++++--------
.../frontend/src/models/miscellaneousAPI.ts | 2 +-
rsconcept/frontend/src/models/rsform.ts | 37 ++++++++++++++---
rsconcept/frontend/src/models/rsformAPI.ts | 8 ++--
.../EditorConstituenta/FormConstituenta.tsx | 2 +-
.../EditorTermGraph/useGraphFilter.ts | 4 +-
.../TableSideConstituents.tsx | 4 +-
rsconcept/frontend/src/utils/codemirror.ts | 8 ++--
14 files changed, 122 insertions(+), 45 deletions(-)
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);
}