R: Add inherited cst source to API
This commit is contained in:
parent
fc669b8b3a
commit
3555db3edf
|
@ -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}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -4,6 +4,7 @@ from .basics import (
|
|||
ASTNodeSerializer,
|
||||
ExpressionParseSerializer,
|
||||
ExpressionSerializer,
|
||||
InheritanceDataSerializer,
|
||||
MultiFormSerializer,
|
||||
ResolverSerializer,
|
||||
TextSerializer,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 ? (
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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[];
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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/> в операционной схеме синтеза'
|
||||
|
|
|
@ -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]) : [])
|
||||
];
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user