mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 13:00:39 +03:00
Add concept derivation relation to backend
This commit is contained in:
parent
6957d3d2fa
commit
f03fd337ba
|
@ -44,6 +44,8 @@ export { BiDownvote as IconMoveDown } from 'react-icons/bi';
|
||||||
|
|
||||||
export { LuRotate3D as IconRotate3D } from 'react-icons/lu';
|
export { LuRotate3D as IconRotate3D } from 'react-icons/lu';
|
||||||
export { MdOutlineFitScreen as IconFitImage } from 'react-icons/md';
|
export { MdOutlineFitScreen as IconFitImage } from 'react-icons/md';
|
||||||
|
export { LuSparkles as IconClustering } from 'react-icons/lu';
|
||||||
|
export { LuSparkle as IconClusteringOff } from 'react-icons/lu';
|
||||||
|
|
||||||
interface IconSVGProps {
|
interface IconSVGProps {
|
||||||
viewBox: string;
|
viewBox: string;
|
||||||
|
|
|
@ -13,6 +13,18 @@ function InfoConstituenta({ data, className, ...restProps }: InfoConstituentaPro
|
||||||
return (
|
return (
|
||||||
<div className={clsx('dense', className)} {...restProps}>
|
<div className={clsx('dense', className)} {...restProps}>
|
||||||
<h2>Конституента {data.alias}</h2>
|
<h2>Конституента {data.alias}</h2>
|
||||||
|
{data.derived_alias ? (
|
||||||
|
<p>
|
||||||
|
<b>Основана на: </b>
|
||||||
|
{data.derived_alias}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
{data.derived_children.length > 0 ? (
|
||||||
|
<p>
|
||||||
|
<b>Порождает: </b>
|
||||||
|
{data.derived_children.join(', ')}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
<p>
|
<p>
|
||||||
<b>Типизация: </b>
|
<b>Типизация: </b>
|
||||||
{labelCstTypification(data)}
|
{labelCstTypification(data)}
|
||||||
|
|
|
@ -55,6 +55,12 @@ function DlgGraphParams({ hideWindow, initial, onConfirm }: DlgGraphParamsProps)
|
||||||
value={params.noTransitive}
|
value={params.noTransitive}
|
||||||
setValue={value => updateParams({ noTransitive: value })}
|
setValue={value => updateParams({ noTransitive: value })}
|
||||||
/>
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label='Свернуть производные'
|
||||||
|
title='Отображать производные понятия вместе с основным'
|
||||||
|
value={params.foldDerived}
|
||||||
|
setValue={value => updateParams({ foldDerived: value })}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex flex-col gap-1'>
|
<div className='flex flex-col gap-1'>
|
||||||
<h1 className='mb-2'>Типы конституент</h1>
|
<h1 className='mb-2'>Типы конституент</h1>
|
||||||
|
|
|
@ -69,6 +69,10 @@ export class Graph {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
at(target: number): GraphNode | undefined {
|
||||||
|
return this.nodes.get(target);
|
||||||
|
}
|
||||||
|
|
||||||
addNode(target: number): GraphNode {
|
addNode(target: number): GraphNode {
|
||||||
let node = this.nodes.get(target);
|
let node = this.nodes.get(target);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
|
|
|
@ -96,6 +96,7 @@ export interface GraphFilterParams {
|
||||||
noTransitive: boolean;
|
noTransitive: boolean;
|
||||||
noTemplates: boolean;
|
noTemplates: boolean;
|
||||||
noText: boolean;
|
noText: boolean;
|
||||||
|
foldDerived: boolean;
|
||||||
|
|
||||||
allowBase: boolean;
|
allowBase: boolean;
|
||||||
allowStruct: boolean;
|
allowStruct: boolean;
|
||||||
|
|
|
@ -96,6 +96,9 @@ export interface IConstituenta extends IConstituentaMeta {
|
||||||
cst_class: CstClass;
|
cst_class: CstClass;
|
||||||
status: ExpressionStatus;
|
status: ExpressionStatus;
|
||||||
is_template: boolean;
|
is_template: boolean;
|
||||||
|
derived_from: ConstituentaID;
|
||||||
|
derived_alias?: string;
|
||||||
|
derived_children: string[];
|
||||||
parse: {
|
parse: {
|
||||||
status: ParsingStatus;
|
status: ParsingStatus;
|
||||||
valueClass: ValueClass;
|
valueClass: ValueClass;
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
IRSFormStats
|
IRSFormStats
|
||||||
} from './rsform';
|
} from './rsform';
|
||||||
import { ParsingStatus, ValueClass } from './rslang';
|
import { ParsingStatus, ValueClass } from './rslang';
|
||||||
import { extractGlobals } from './rslangAPI';
|
import { extractGlobals, isSimpleExpression, splitTemplateDefinition } from './rslangAPI';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads data into an {@link IRSForm} based on {@link IRSFormData}.
|
* Loads data into an {@link IRSForm} based on {@link IRSFormData}.
|
||||||
|
@ -32,7 +32,11 @@ export function loadRSFormData(input: IRSFormData): IRSForm {
|
||||||
result.graph = new Graph();
|
result.graph = new Graph();
|
||||||
result.stats = calculateStats(result.items);
|
result.stats = calculateStats(result.items);
|
||||||
|
|
||||||
|
const derivationLookup: Map<ConstituentaID, ConstituentaID> = new Map();
|
||||||
result.items.forEach(cst => {
|
result.items.forEach(cst => {
|
||||||
|
derivationLookup.set(cst.id, cst.id);
|
||||||
|
cst.derived_from = cst.id;
|
||||||
|
cst.derived_children = [];
|
||||||
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
|
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
|
||||||
cst.is_template = inferTemplate(cst.definition_formal);
|
cst.is_template = inferTemplate(cst.definition_formal);
|
||||||
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
cst.cst_class = inferClass(cst.cst_type, cst.is_template);
|
||||||
|
@ -45,6 +49,34 @@ export function loadRSFormData(input: IRSFormData): IRSForm {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
// Calculate derivation of constituents based on formal definition analysis
|
||||||
|
result.graph.topologicalOrder().forEach(id => {
|
||||||
|
const cst = result.items.find(item => item.id === id)!;
|
||||||
|
const resolvedInput: Set<ConstituentaID> = new Set();
|
||||||
|
let definition = '';
|
||||||
|
if (!isFunctional(cst.cst_type)) {
|
||||||
|
const node = result.graph.at(id)!;
|
||||||
|
node.inputs.forEach(id => resolvedInput.add(derivationLookup.get(id)!));
|
||||||
|
definition = cst.definition_formal;
|
||||||
|
} else {
|
||||||
|
const expression = splitTemplateDefinition(cst.definition_formal);
|
||||||
|
definition = expression.body;
|
||||||
|
const dependencies = extractGlobals(definition);
|
||||||
|
dependencies.forEach(alias => {
|
||||||
|
const targetCst = result.items.find(item => item.alias === alias);
|
||||||
|
if (targetCst) {
|
||||||
|
resolvedInput.add(derivationLookup.get(targetCst.id)!);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (resolvedInput.size === 1 && isSimpleExpression(definition)) {
|
||||||
|
const parent = result.items.find(item => item.id === resolvedInput.values().next().value)!;
|
||||||
|
cst.derived_from = parent.id;
|
||||||
|
cst.derived_alias = parent.alias;
|
||||||
|
parent.derived_children.push(cst.alias);
|
||||||
|
derivationLookup.set(cst.id, parent.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,6 +206,8 @@ export function inferClass(type: CstType, isTemplate: boolean): CstClass {
|
||||||
export function createMockConstituenta(id: ConstituentaID, alias: string, comment: string): IConstituenta {
|
export function createMockConstituenta(id: ConstituentaID, alias: string, comment: string): IConstituenta {
|
||||||
return {
|
return {
|
||||||
id: id,
|
id: id,
|
||||||
|
derived_from: id,
|
||||||
|
derived_children: [],
|
||||||
order: -1,
|
order: -1,
|
||||||
schema: -1,
|
schema: -1,
|
||||||
alias: alias,
|
alias: alias,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { extractGlobals, splitTemplateDefinition } from './rslangAPI';
|
import { extractGlobals, isSimpleExpression, splitTemplateDefinition } from './rslangAPI';
|
||||||
|
|
||||||
const globalsData = [
|
const globalsData = [
|
||||||
['', ''],
|
['', ''],
|
||||||
|
@ -13,6 +13,23 @@ describe('Testing extract globals', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const simpleExpressionData = [
|
||||||
|
['', 'true'],
|
||||||
|
['Pr1(S1)', 'true'],
|
||||||
|
['pr1(S1)', 'true'],
|
||||||
|
['red(S1)', 'true'],
|
||||||
|
['red(Pr1(F1[α,σ]))', 'true'],
|
||||||
|
['D{(α,β)∈D6×D6 | α≠β & α∩β≠∅}', 'false'],
|
||||||
|
['I{(β,α) | α:∈D2; σ:=F5[α]; β:∈σ}', 'false'],
|
||||||
|
['∀σ∈S1 (F1[σ]×F1[σ])∩D11=∅', 'false']
|
||||||
|
];
|
||||||
|
describe('Testing simple expression', () => {
|
||||||
|
it.each(simpleExpressionData)('isSimpleExpression %p', (input: string, expected: string) => {
|
||||||
|
const result = isSimpleExpression(input);
|
||||||
|
expect(String(result)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const splitData = [
|
const splitData = [
|
||||||
['', '||'],
|
['', '||'],
|
||||||
['[α∈ℬ(R1)] α⊆red(σ)', 'α∈ℬ(R1)||α⊆red(σ)'],
|
['[α∈ℬ(R1)] α⊆red(σ)', 'α∈ℬ(R1)||α⊆red(σ)'],
|
||||||
|
|
|
@ -7,14 +7,24 @@ import { applyPattern } from '@/utils/utils';
|
||||||
import { CstType } from './rsform';
|
import { CstType } from './rsform';
|
||||||
import { IArgumentValue, IRSErrorDescription, RSErrorClass, RSErrorType } from './rslang';
|
import { IArgumentValue, IRSErrorDescription, RSErrorClass, RSErrorType } from './rslang';
|
||||||
|
|
||||||
|
// cspell:disable
|
||||||
const LOCALS_REGEXP = /[_a-zα-ω][a-zα-ω]*\d*/g;
|
const LOCALS_REGEXP = /[_a-zα-ω][a-zα-ω]*\d*/g;
|
||||||
|
const GLOBALS_REGEXP = /[XCSADFPT]\d+/g;
|
||||||
|
const COMPLEX_SYMBOLS_REGEXP = /[∀∃×ℬ;|:]/g;
|
||||||
|
// cspell:enable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts global variable names from a given expression.
|
* Extracts global variable names from a given expression.
|
||||||
*/
|
*/
|
||||||
export function extractGlobals(expression: string): Set<string> {
|
export function extractGlobals(expression: string): Set<string> {
|
||||||
// cspell:disable-next-line
|
return new Set(expression.match(GLOBALS_REGEXP) ?? []);
|
||||||
return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if expression is simple derivation.
|
||||||
|
*/
|
||||||
|
export function isSimpleExpression(text: string): boolean {
|
||||||
|
return !COMPLEX_SYMBOLS_REGEXP.test(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -36,6 +36,7 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||||
noTemplates: false,
|
noTemplates: false,
|
||||||
noTransitive: true,
|
noTransitive: true,
|
||||||
noText: false,
|
noText: false,
|
||||||
|
foldDerived: false,
|
||||||
|
|
||||||
allowBase: true,
|
allowBase: true,
|
||||||
allowStruct: true,
|
allowStruct: true,
|
||||||
|
@ -217,11 +218,18 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
||||||
is3D={is3D}
|
is3D={is3D}
|
||||||
orbit={orbit}
|
orbit={orbit}
|
||||||
noText={filterParams.noText}
|
noText={filterParams.noText}
|
||||||
|
foldDerived={filterParams.foldDerived}
|
||||||
showParamsDialog={() => setShowParamsDialog(true)}
|
showParamsDialog={() => setShowParamsDialog(true)}
|
||||||
onCreate={handleCreateCst}
|
onCreate={handleCreateCst}
|
||||||
onDelete={handleDeleteCst}
|
onDelete={handleDeleteCst}
|
||||||
onResetViewpoint={() => setToggleResetView(prev => !prev)}
|
onResetViewpoint={() => setToggleResetView(prev => !prev)}
|
||||||
toggleOrbit={() => setOrbit(prev => !prev)}
|
toggleOrbit={() => setOrbit(prev => !prev)}
|
||||||
|
toggleFoldDerived={() =>
|
||||||
|
setFilterParams(prev => ({
|
||||||
|
...prev,
|
||||||
|
foldDerived: !prev.foldDerived
|
||||||
|
}))
|
||||||
|
}
|
||||||
toggleNoText={() =>
|
toggleNoText={() =>
|
||||||
setFilterParams(prev => ({
|
setFilterParams(prev => ({
|
||||||
...prev,
|
...prev,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
IconClustering,
|
||||||
|
IconClusteringOff,
|
||||||
IconDestroy,
|
IconDestroy,
|
||||||
IconFilter,
|
IconFilter,
|
||||||
IconFitImage,
|
IconFitImage,
|
||||||
|
@ -22,12 +24,14 @@ interface GraphToolbarProps {
|
||||||
|
|
||||||
orbit: boolean;
|
orbit: boolean;
|
||||||
noText: boolean;
|
noText: boolean;
|
||||||
|
foldDerived: boolean;
|
||||||
|
|
||||||
showParamsDialog: () => void;
|
showParamsDialog: () => void;
|
||||||
onCreate: () => void;
|
onCreate: () => void;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
onResetViewpoint: () => void;
|
onResetViewpoint: () => void;
|
||||||
|
|
||||||
|
toggleFoldDerived: () => void;
|
||||||
toggleNoText: () => void;
|
toggleNoText: () => void;
|
||||||
toggleOrbit: () => void;
|
toggleOrbit: () => void;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +39,9 @@ interface GraphToolbarProps {
|
||||||
function GraphToolbar({
|
function GraphToolbar({
|
||||||
is3D,
|
is3D,
|
||||||
noText,
|
noText,
|
||||||
|
foldDerived,
|
||||||
toggleNoText,
|
toggleNoText,
|
||||||
|
toggleFoldDerived,
|
||||||
orbit,
|
orbit,
|
||||||
toggleOrbit,
|
toggleOrbit,
|
||||||
showParamsDialog,
|
showParamsDialog,
|
||||||
|
@ -67,6 +73,17 @@ function GraphToolbar({
|
||||||
}
|
}
|
||||||
onClick={toggleNoText}
|
onClick={toggleNoText}
|
||||||
/>
|
/>
|
||||||
|
<MiniButton
|
||||||
|
title={!foldDerived ? 'Скрыть производные' : 'Отображать производные'}
|
||||||
|
icon={
|
||||||
|
!foldDerived ? (
|
||||||
|
<IconClustering size='1.25rem' className='icon-green' />
|
||||||
|
) : (
|
||||||
|
<IconClusteringOff size='1.25rem' className='icon-primary' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={toggleFoldDerived}
|
||||||
|
/>
|
||||||
<MiniButton
|
<MiniButton
|
||||||
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
|
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
|
||||||
title='Граф целиком'
|
title='Граф целиком'
|
||||||
|
|
|
@ -46,6 +46,13 @@ function useGraphFilter(schema: IRSForm | undefined, params: GraphFilterParams)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (params.foldDerived) {
|
||||||
|
schema.items.forEach(cst => {
|
||||||
|
if (cst.derived_alias) {
|
||||||
|
graph.foldNode(cst.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
setFiltered(graph);
|
setFiltered(graph);
|
||||||
}, [schema, params, allowedTypes]);
|
}, [schema, params, allowedTypes]);
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ export const storage = {
|
||||||
librarySearchStrategy: 'library.search.strategy',
|
librarySearchStrategy: 'library.search.strategy',
|
||||||
libraryPagination: 'library.pagination',
|
libraryPagination: 'library.pagination',
|
||||||
|
|
||||||
rsgraphFilter: 'rsgraph.filter',
|
rsgraphFilter: 'rsgraph.filter_options',
|
||||||
rsgraphLayout: 'rsgraph.layout',
|
rsgraphLayout: 'rsgraph.layout',
|
||||||
rsgraphColoringScheme: 'rsgraph.coloring_scheme',
|
rsgraphColoringScheme: 'rsgraph.coloring_scheme',
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user