mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +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 { 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 {
|
||||
viewBox: string;
|
||||
|
|
|
@ -13,6 +13,18 @@ function InfoConstituenta({ data, className, ...restProps }: InfoConstituentaPro
|
|||
return (
|
||||
<div className={clsx('dense', className)} {...restProps}>
|
||||
<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>
|
||||
<b>Типизация: </b>
|
||||
{labelCstTypification(data)}
|
||||
|
|
|
@ -55,6 +55,12 @@ function DlgGraphParams({ hideWindow, initial, onConfirm }: DlgGraphParamsProps)
|
|||
value={params.noTransitive}
|
||||
setValue={value => updateParams({ noTransitive: value })}
|
||||
/>
|
||||
<Checkbox
|
||||
label='Свернуть производные'
|
||||
title='Отображать производные понятия вместе с основным'
|
||||
value={params.foldDerived}
|
||||
setValue={value => updateParams({ foldDerived: value })}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex flex-col gap-1'>
|
||||
<h1 className='mb-2'>Типы конституент</h1>
|
||||
|
|
|
@ -69,6 +69,10 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
at(target: number): GraphNode | undefined {
|
||||
return this.nodes.get(target);
|
||||
}
|
||||
|
||||
addNode(target: number): GraphNode {
|
||||
let node = this.nodes.get(target);
|
||||
if (!node) {
|
||||
|
|
|
@ -96,6 +96,7 @@ export interface GraphFilterParams {
|
|||
noTransitive: boolean;
|
||||
noTemplates: boolean;
|
||||
noText: boolean;
|
||||
foldDerived: boolean;
|
||||
|
||||
allowBase: boolean;
|
||||
allowStruct: boolean;
|
||||
|
|
|
@ -96,6 +96,9 @@ export interface IConstituenta extends IConstituentaMeta {
|
|||
cst_class: CstClass;
|
||||
status: ExpressionStatus;
|
||||
is_template: boolean;
|
||||
derived_from: ConstituentaID;
|
||||
derived_alias?: string;
|
||||
derived_children: string[];
|
||||
parse: {
|
||||
status: ParsingStatus;
|
||||
valueClass: ValueClass;
|
||||
|
|
|
@ -18,7 +18,7 @@ import {
|
|||
IRSFormStats
|
||||
} from './rsform';
|
||||
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}.
|
||||
|
@ -32,7 +32,11 @@ export function loadRSFormData(input: IRSFormData): IRSForm {
|
|||
result.graph = new Graph();
|
||||
result.stats = calculateStats(result.items);
|
||||
|
||||
const derivationLookup: Map<ConstituentaID, ConstituentaID> = new Map();
|
||||
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.is_template = inferTemplate(cst.definition_formal);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -174,6 +206,8 @@ export function inferClass(type: CstType, isTemplate: boolean): CstClass {
|
|||
export function createMockConstituenta(id: ConstituentaID, alias: string, comment: string): IConstituenta {
|
||||
return {
|
||||
id: id,
|
||||
derived_from: id,
|
||||
derived_children: [],
|
||||
order: -1,
|
||||
schema: -1,
|
||||
alias: alias,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { extractGlobals, splitTemplateDefinition } from './rslangAPI';
|
||||
import { extractGlobals, isSimpleExpression, splitTemplateDefinition } from './rslangAPI';
|
||||
|
||||
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 = [
|
||||
['', '||'],
|
||||
['[α∈ℬ(R1)] α⊆red(σ)', 'α∈ℬ(R1)||α⊆red(σ)'],
|
||||
|
|
|
@ -7,14 +7,24 @@ import { applyPattern } from '@/utils/utils';
|
|||
import { CstType } from './rsform';
|
||||
import { IArgumentValue, IRSErrorDescription, RSErrorClass, RSErrorType } from './rslang';
|
||||
|
||||
// cspell:disable
|
||||
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.
|
||||
*/
|
||||
export function extractGlobals(expression: string): Set<string> {
|
||||
// cspell:disable-next-line
|
||||
return new Set(expression.match(/[XCSADFPT]\d+/g) ?? []);
|
||||
return new Set(expression.match(GLOBALS_REGEXP) ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
noTransitive: true,
|
||||
noText: false,
|
||||
foldDerived: false,
|
||||
|
||||
allowBase: true,
|
||||
allowStruct: true,
|
||||
|
@ -217,11 +218,18 @@ function EditorTermGraph({ onOpenEdit }: EditorTermGraphProps) {
|
|||
is3D={is3D}
|
||||
orbit={orbit}
|
||||
noText={filterParams.noText}
|
||||
foldDerived={filterParams.foldDerived}
|
||||
showParamsDialog={() => setShowParamsDialog(true)}
|
||||
onCreate={handleCreateCst}
|
||||
onDelete={handleDeleteCst}
|
||||
onResetViewpoint={() => setToggleResetView(prev => !prev)}
|
||||
toggleOrbit={() => setOrbit(prev => !prev)}
|
||||
toggleFoldDerived={() =>
|
||||
setFilterParams(prev => ({
|
||||
...prev,
|
||||
foldDerived: !prev.foldDerived
|
||||
}))
|
||||
}
|
||||
toggleNoText={() =>
|
||||
setFilterParams(prev => ({
|
||||
...prev,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import {
|
||||
IconClustering,
|
||||
IconClusteringOff,
|
||||
IconDestroy,
|
||||
IconFilter,
|
||||
IconFitImage,
|
||||
|
@ -22,12 +24,14 @@ interface GraphToolbarProps {
|
|||
|
||||
orbit: boolean;
|
||||
noText: boolean;
|
||||
foldDerived: boolean;
|
||||
|
||||
showParamsDialog: () => void;
|
||||
onCreate: () => void;
|
||||
onDelete: () => void;
|
||||
onResetViewpoint: () => void;
|
||||
|
||||
toggleFoldDerived: () => void;
|
||||
toggleNoText: () => void;
|
||||
toggleOrbit: () => void;
|
||||
}
|
||||
|
@ -35,7 +39,9 @@ interface GraphToolbarProps {
|
|||
function GraphToolbar({
|
||||
is3D,
|
||||
noText,
|
||||
foldDerived,
|
||||
toggleNoText,
|
||||
toggleFoldDerived,
|
||||
orbit,
|
||||
toggleOrbit,
|
||||
showParamsDialog,
|
||||
|
@ -67,6 +73,17 @@ function GraphToolbar({
|
|||
}
|
||||
onClick={toggleNoText}
|
||||
/>
|
||||
<MiniButton
|
||||
title={!foldDerived ? 'Скрыть производные' : 'Отображать производные'}
|
||||
icon={
|
||||
!foldDerived ? (
|
||||
<IconClustering size='1.25rem' className='icon-green' />
|
||||
) : (
|
||||
<IconClusteringOff size='1.25rem' className='icon-primary' />
|
||||
)
|
||||
}
|
||||
onClick={toggleFoldDerived}
|
||||
/>
|
||||
<MiniButton
|
||||
icon={<IconFitImage size='1.25rem' className='icon-primary' />}
|
||||
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);
|
||||
}, [schema, params, allowedTypes]);
|
||||
|
||||
|
|
|
@ -92,7 +92,7 @@ export const storage = {
|
|||
librarySearchStrategy: 'library.search.strategy',
|
||||
libraryPagination: 'library.pagination',
|
||||
|
||||
rsgraphFilter: 'rsgraph.filter',
|
||||
rsgraphFilter: 'rsgraph.filter_options',
|
||||
rsgraphLayout: 'rsgraph.layout',
|
||||
rsgraphColoringScheme: 'rsgraph.coloring_scheme',
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user