R: Add inherited cst source to API

This commit is contained in:
Ivan 2024-09-14 15:14:48 +03:00
parent fc669b8b3a
commit 3555db3edf
14 changed files with 122 additions and 45 deletions

View File

@ -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}

View File

@ -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 = {

View File

@ -4,6 +4,7 @@ from .basics import (
ASTNodeSerializer,
ExpressionParseSerializer,
ExpressionSerializer,
InheritanceDataSerializer,
MultiFormSerializer,
ResolverSerializer,
TextSerializer,

View File

@ -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()

View File

@ -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:

View File

@ -39,16 +39,16 @@ function InfoConstituenta({ data, className, ...restProps }: InfoConstituentaPro
{data.definition_resolved}
</p>
) : null}
{data.parent_alias ? (
{data.spawner_alias ? (
<p>
<b>Основание: </b>
{data.parent_alias}
{data.spawner_alias}
</p>
) : null}
{data.children_alias.length > 0 ? (
{data.spawn_alias.length > 0 ? (
<p>
<b>Порождает: </b>
{data.children_alias.join(', ')}
{data.spawn_alias.join(', ')}
</p>
) : null}
{data.convention ? (

View File

@ -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<ConstituentaID, LibraryItemID>();
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);
}
});
}

View File

@ -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;
}
}

View File

@ -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[];
}

View File

@ -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,

View File

@ -249,7 +249,7 @@ function FormConstituenta({
icon={<IconSave size='1.25rem' />}
/>
<Overlay position='top-[0.1rem] left-[0.4rem]' className='cc-icons'>
{state.is_inherited_parent && !state.is_inherited ? (
{state.has_inherited_children && !state.is_inherited ? (
<Indicator
icon={<IconPredecessor size='1.25rem' className='clr-text-primary' />}
titleHtml='Внимание!</br> Конституента имеет потомков<br/> в операционной схеме синтеза'

View File

@ -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]) : [])
];

View File

@ -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
}

View File

@ -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 = `<b>Основание:</b> ${cst.parent_alias}`;
derived.innerHTML = `<b>Основание:</b> ${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 = `<b>Порождает:</b> ${cst.children_alias.join(', ')}`;
children.innerHTML = `<b>Порождает:</b> ${cst.spawn_alias.join(', ')}`;
dom.appendChild(children);
}