Revert changing ID types and minor ui fixes

This commit is contained in:
IRBorisov 2023-08-22 22:38:27 +03:00
parent 503ad5d0dc
commit b85f057c03
16 changed files with 191 additions and 139 deletions

View File

@ -14,8 +14,8 @@ interface ILibraryContext {
filter: (params: ILibraryFilter) => IRSFormMeta[] filter: (params: ILibraryFilter) => IRSFormMeta[]
createSchema: (data: IRSFormCreateData, callback?: DataCallback<IRSFormMeta>) => void createSchema: (data: IRSFormCreateData, callback?: DataCallback<IRSFormMeta>) => void
cloneSchema: (target: string, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => void cloneSchema: (target: number, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => void
destroySchema: (target: string, callback?: () => void) => void destroySchema: (target: number, callback?: () => void) => void
} }
const LibraryContext = createContext<ILibraryContext | null>(null) const LibraryContext = createContext<ILibraryContext | null>(null)
@ -62,8 +62,8 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
getLibrary({ getLibrary({
setLoading: setLoading, setLoading: setLoading,
showError: true, showError: true,
onError: (error) => setError(error), onError: error => setError(error),
onSuccess: newData => { onSuccess: newData => {
setItems(newData); setItems(newData);
if (callback) callback(); if (callback) callback();
} }
@ -90,9 +90,9 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
}, [reload]); }, [reload]);
const destroySchema = useCallback( const destroySchema = useCallback(
(target: string, callback?: () => void) => { (target: number, callback?: () => void) => {
setError(undefined) setError(undefined)
deleteRSForm(target, { deleteRSForm(String(target), {
showError: true, showError: true,
setLoading: setProcessing, setLoading: setProcessing,
onError: error => setError(error), onError: error => setError(error),
@ -103,12 +103,12 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
}, [setError, reload]); }, [setError, reload]);
const cloneSchema = useCallback( const cloneSchema = useCallback(
(target: string, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => { (target: number, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => {
if (!user) { if (!user) {
return; return;
} }
setError(undefined) setError(undefined)
postCloneRSForm(target, { postCloneRSForm(String(target), {
data: data, data: data,
showError: true, showError: true,
setLoading: setProcessing, setLoading: setProcessing,

View File

@ -205,7 +205,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
const cstUpdate = useCallback( const cstUpdate = useCallback(
(data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => { (data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => {
setError(undefined) setError(undefined)
patchConstituenta(data.id, { patchConstituenta(String(data.id), {
data: data, data: data,
showError: true, showError: true,
setLoading: setProcessing, setLoading: setProcessing,

View File

@ -2,7 +2,29 @@ import { useCallback, useState } from 'react'
import { type ErrorInfo } from '../components/BackendError'; import { type ErrorInfo } from '../components/BackendError';
import { DataCallback, postCheckExpression } from '../utils/backendAPI'; import { DataCallback, postCheckExpression } from '../utils/backendAPI';
import { IExpressionParse, type IRSForm } from '../utils/models'; import { RSErrorType } from '../utils/enums';
import { CstType, IConstituenta, IExpressionParse, IFunctionArg, type IRSForm } from '../utils/models';
import { getCstExpressionPrefix } from '../utils/staticUI';
function checkTypeConsistency(type: CstType, typification: string, args: IFunctionArg[]): boolean {
switch (type) {
case CstType.BASE:
case CstType.CONSTANT:
case CstType.STRUCTURED:
case CstType.TERM:
return typification !== '' && args.length === 0;
case CstType.AXIOM:
case CstType.THEOREM:
return typification === '' && args.length === 0;
case CstType.FUNCTION:
return typification !== '' && args.length !== 0;
case CstType.PREDICATE:
return typification === '' && args.length !== 0;
}
}
function useCheckExpression({ schema }: { schema?: IRSForm }) { function useCheckExpression({ schema }: { schema?: IRSForm }) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -11,16 +33,38 @@ function useCheckExpression({ schema }: { schema?: IRSForm }) {
const resetParse = useCallback(() => { setParseData(undefined); }, []); const resetParse = useCallback(() => { setParseData(undefined); }, []);
function checkExpression(expression: string, onSuccess?: DataCallback<IExpressionParse>) { function checkExpression(expression: string, activeCst?: IConstituenta, onSuccess?: DataCallback<IExpressionParse>) {
setError(undefined); setError(undefined);
postCheckExpression(schema!.id, { postCheckExpression(String(schema!.id), {
data: { expression: expression }, data: { expression: expression },
showError: true, showError: true,
setLoading, setLoading,
onError: error => { setError(error); }, onError: error => { setError(error); },
onSuccess: newData => { onSuccess: parse => {
setParseData(newData); if (activeCst && parse.parseResult) {
if (onSuccess) onSuccess(newData); if (activeCst.cstType == CstType.BASE || activeCst.cstType == CstType.CONSTANT) {
if (expression !== getCstExpressionPrefix(activeCst)) {
parse.parseResult = false;
parse.errors.push({
errorType: RSErrorType.globalNonemptyBase,
isCritical: true,
params: [],
position: 0
});
}
}
if (!checkTypeConsistency(activeCst.cstType, parse.typification, parse.args)) {
parse.parseResult = false;
parse.errors.push({
errorType: RSErrorType.globalUnexpectedType,
isCritical: true,
params: [],
position: 0
});
}
}
setParseData(parse);
if (onSuccess) onSuccess(parse);
} }
}); });
} }

View File

@ -7,15 +7,15 @@ import { getCstLabel } from '../../utils/staticUI';
interface DlgDeleteCstProps { interface DlgDeleteCstProps {
hideWindow: () => void hideWindow: () => void
selected: string[] selected: number[]
onDelete: (items: string[]) => void onDelete: (items: number[]) => void
} }
function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) { function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
const { schema } = useRSForm(); const { schema } = useRSForm();
const [ expandOut, setExpandOut ] = useState(false); const [ expandOut, setExpandOut ] = useState(false);
const expansion: string[] = useMemo(() => schema?.graph.expandOutputs(selected) ?? [], [selected, schema?.graph]); const expansion: number[] = useMemo(() => schema?.graph.expandOutputs(selected) ?? [], [selected, schema?.graph]);
function handleSubmit() { function handleSubmit() {
hideWindow(); hideWindow();

View File

@ -18,11 +18,11 @@ import ViewSideConstituents from './elements/ViewSideConstituents';
const UNFOLDED_HEIGHT = '59.1rem'; const UNFOLDED_HEIGHT = '59.1rem';
interface EditorConstituentaProps { interface EditorConstituentaProps {
activeID?: string activeID?: number
onOpenEdit: (cstID: string) => void onOpenEdit: (cstID: number) => void
onShowAST: (expression: string, ast: SyntaxTree) => void onShowAST: (expression: string, ast: SyntaxTree) => void
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
onDeleteCst: (selected: string[], callback?: (items: string[]) => void) => void onDeleteCst: (selected: number[], callback?: (items: number[]) => void) => void
} }
function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDeleteCst }: EditorConstituentaProps) { function EditorConstituenta({ activeID, onShowAST, onCreateCst, onOpenEdit, onDeleteCst }: EditorConstituentaProps) {

View File

@ -14,15 +14,15 @@ import { CstType, IConstituenta, ICstCreateData, ICstMovetoData } from '../../ut
import { getCstTypePrefix, getCstTypeShortcut, getCstTypificationLabel, mapStatusInfo } from '../../utils/staticUI'; import { getCstTypePrefix, getCstTypeShortcut, getCstTypificationLabel, mapStatusInfo } from '../../utils/staticUI';
interface EditorItemsProps { interface EditorItemsProps {
onOpenEdit: (cstID: string) => void onOpenEdit: (cstID: number) => void
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
onDeleteCst: (selected: string[], callback: (items: string[]) => void) => void onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void
} }
function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps) { function EditorItems({ onOpenEdit, onCreateCst, onDeleteCst }: EditorItemsProps) {
const { schema, isEditable, cstMoveTo, resetAliases } = useRSForm(); const { schema, isEditable, cstMoveTo, resetAliases } = useRSForm();
const { noNavigation } = useConceptTheme(); const { noNavigation } = useConceptTheme();
const [selected, setSelected] = useState<string[]>([]); const [selected, setSelected] = useState<number[]>([]);
const nothingSelected = useMemo(() => selected.length === 0, [selected]); const nothingSelected = useMemo(() => selected.length === 0, [selected]);
const [toggledClearRows, setToggledClearRows] = useState(false); const [toggledClearRows, setToggledClearRows] = useState(false);

View File

@ -60,7 +60,7 @@ function EditorRSExpression({
} }
const prefix = getCstExpressionPrefix(activeCst); const prefix = getCstExpressionPrefix(activeCst);
const expression = prefix + value; const expression = prefix + value;
checkExpression(expression, parse => { checkExpression(expression, activeCst, parse => {
if (parse.errors.length > 0) { if (parse.errors.length > 0) {
onShowError(parse.errors[0]); onShowError(parse.errors[0]);
} else { } else {
@ -81,7 +81,8 @@ function EditorRSExpression({
return; return;
} }
const prefix = getCstExpressionPrefix(activeCst); const prefix = getCstExpressionPrefix(activeCst);
const errorPosition = error.position - prefix.length; let errorPosition = error.position - prefix.length;
if (errorPosition < 0) errorPosition = 0;
rsInput.current?.view?.dispatch({ rsInput.current?.view?.dispatch({
selection: { selection: {
anchor: errorPosition, anchor: errorPosition,

View File

@ -56,9 +56,9 @@ export interface GraphEditorParams {
} }
interface EditorTermGraphProps { interface EditorTermGraphProps {
onOpenEdit: (cstID: string) => void onOpenEdit: (cstID: number) => void
onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void onCreateCst: (initial: ICstCreateData, skipDialog?: boolean) => void
onDeleteCst: (selected: string[], callback: (items: string[]) => void) => void onDeleteCst: (selected: number[], callback: (items: number[]) => void) => void
} }
function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGraphProps) { function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGraphProps) {
@ -83,16 +83,16 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
const [ allowTheorem, setAllowTheorem ] = useLocalStorage('graph_allow_theorem', true); const [ allowTheorem, setAllowTheorem ] = useLocalStorage('graph_allow_theorem', true);
const [ filtered, setFiltered ] = useState<Graph>(new Graph()); const [ filtered, setFiltered ] = useState<Graph>(new Graph());
const [ dismissed, setDismissed ] = useState<string[]>([]); const [ dismissed, setDismissed ] = useState<number[]>([]);
const [ selectedDismissed, setSelectedDismissed ] = useState<string[]>([]); const [ selectedDismissed, setSelectedDismissed ] = useState<number[]>([]);
const graphRef = useRef<GraphCanvasRef | null>(null); const graphRef = useRef<GraphCanvasRef | null>(null);
const [showOptions, setShowOptions] = useState(false); const [showOptions, setShowOptions] = useState(false);
const [toggleUpdate, setToggleUpdate] = useState(false); const [toggleUpdate, setToggleUpdate] = useState(false);
const [hoverID, setHoverID] = useState<string | undefined>(undefined); const [hoverID, setHoverID] = useState<number | undefined>(undefined);
const hoverCst = useMemo( const hoverCst = useMemo(
() => { () => {
return schema?.items.find(cst => String(cst.id) === hoverID); return schema?.items.find(cst => cst.id === hoverID);
}, [schema?.items, hoverID]); }, [schema?.items, hoverID]);
const is3D = useMemo(() => layout.includes('3d'), [layout]); const is3D = useMemo(() => layout.includes('3d'), [layout]);
@ -137,7 +137,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
} }
}); });
} }
const newDismissed: string[] = []; const newDismissed: number[] = [];
schema.items.forEach(cst => { schema.items.forEach(cst => {
if (!graph.nodes.has(cst.id)) { if (!graph.nodes.has(cst.id)) {
newDismissed.push(cst.id); newDismissed.push(cst.id);
@ -149,7 +149,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
setHoverID(undefined); setHoverID(undefined);
}, [schema, noHermits, noTransitive, noTemplates, allowedTypes, toggleUpdate]); }, [schema, noHermits, noTransitive, noTemplates, allowedTypes, toggleUpdate]);
function toggleDismissed(cstID: string) { function toggleDismissed(cstID: number) {
setSelectedDismissed(prev => { setSelectedDismissed(prev => {
const index = prev.findIndex(id => cstID === id); const index = prev.findIndex(id => cstID === id);
if (index !== -1) { if (index !== -1) {
@ -171,7 +171,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
const cst = schema.items.find(cst => cst.id === node.id); const cst = schema.items.find(cst => cst.id === node.id);
if (cst) { if (cst) {
result.push({ result.push({
id: node.id, id: String(node.id),
fill: getCstNodeColor(cst, coloringScheme, darkMode), fill: getCstNodeColor(cst, coloringScheme, darkMode),
label: cst.term.resolved && !noTerms ? `${cst.alias}: ${cst.term.resolved}` : cst.alias label: cst.term.resolved && !noTerms ? `${cst.alias}: ${cst.term.resolved}` : cst.alias
}); });
@ -189,8 +189,8 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
source.outputs.forEach(target => { source.outputs.forEach(target => {
result.push({ result.push({
id: String(edgeID), id: String(edgeID),
source: source.id, source: String(source.id),
target: target target: String(target)
}); });
edgeID += 1; edgeID += 1;
}); });
@ -216,9 +216,9 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
focusOnSelect: false focusOnSelect: false
}); });
const allSelected: string[] = useMemo( const allSelected: number[] = useMemo(
() => { () => {
return [ ... selectedDismissed, ... selections]; return [ ... selectedDismissed, ... selections.map(id => Number(id))];
}, [selectedDismissed, selections]); }, [selectedDismissed, selections]);
const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]); const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]);
@ -230,7 +230,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
const handleHoverIn = useCallback( const handleHoverIn = useCallback(
(node: GraphNode) => { (node: GraphNode) => {
setHoverID(node.id); setHoverID(Number(node.id));
if (onNodePointerOver) onNodePointerOver(node); if (onNodePointerOver) onNodePointerOver(node);
}, [onNodePointerOver]); }, [onNodePointerOver]);
@ -243,7 +243,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
const handleNodeClick = useCallback( const handleNodeClick = useCallback(
(node: GraphNode) => { (node: GraphNode) => {
if (selections.includes(node.id)) { if (selections.includes(node.id)) {
onOpenEdit(node.id); onOpenEdit(Number(node.id));
return; return;
} }
if (onNodeClick) onNodeClick(node); if (onNodeClick) onNodeClick(node);
@ -346,7 +346,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
}, [noNavigation]); }, [noNavigation]);
const dismissedStyle = useCallback( const dismissedStyle = useCallback(
(cstID: string) => { (cstID: number) => {
return selectedDismissed.includes(cstID) ? {outlineWidth: '2px', outlineStyle: 'solid'}: {}; return selectedDismissed.includes(cstID) ? {outlineWidth: '2px', outlineStyle: 'solid'}: {};
}, [selectedDismissed]); }, [selectedDismissed]);
@ -358,7 +358,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
initial={getOptions()} initial={getOptions()}
onConfirm={handleChangeOptions} onConfirm={handleChangeOptions}
/>} />}
<div className='flex flex-col border-t border-r max-w-[12.44rem] pr-2 pb-2 text-sm select-none' style={{height: canvasHeight}}> <div className='flex flex-col border-t border-r max-w-[12.5rem] pr-2 pb-2 text-sm select-none' style={{height: canvasHeight}}>
{hoverCst && {hoverCst &&
<div className='relative'> <div className='relative'>
<InfoConstituenta <InfoConstituenta
@ -408,10 +408,10 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
</div> </div>
<ConceptSelect <ConceptSelect
className='mt-1 w-fit' className='w-full mt-1'
options={GraphLayoutSelector} options={GraphLayoutSelector}
searchable={false} searchable={false}
placeholder='Выберите тип' placeholder='Способ расположения'
values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []} values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []}
onChange={data => { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }} onChange={data => { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }}
/> />

View File

@ -40,7 +40,7 @@ function RSTabs() {
const { destroySchema } = useLibrary(); const { destroySchema } = useLibrary();
const [activeTab, setActiveTab] = useState(RSTabsList.CARD); const [activeTab, setActiveTab] = useState(RSTabsList.CARD);
const [activeID, setActiveID] = useState<string | undefined>(undefined); const [activeID, setActiveID] = useState<number | undefined>(undefined);
const [showUpload, setShowUpload] = useState(false); const [showUpload, setShowUpload] = useState(false);
const [showClone, setShowClone] = useState(false); const [showClone, setShowClone] = useState(false);
@ -49,8 +49,8 @@ function RSTabs() {
const [expression, setExpression] = useState(''); const [expression, setExpression] = useState('');
const [showAST, setShowAST] = useState(false); const [showAST, setShowAST] = useState(false);
const [afterDelete, setAfterDelete] = useState<((items: string[]) => void) | undefined>(undefined); const [afterDelete, setAfterDelete] = useState<((items: number[]) => void) | undefined>(undefined);
const [toBeDeleted, setToBeDeleted] = useState<string[]>([]); const [toBeDeleted, setToBeDeleted] = useState<number[]>([]);
const [showDeleteCst, setShowDeleteCst] = useState(false); const [showDeleteCst, setShowDeleteCst] = useState(false);
const [createInitialData, setCreateInitialData] = useState<ICstCreateData>(); const [createInitialData, setCreateInitialData] = useState<ICstCreateData>();
@ -70,7 +70,7 @@ function RSTabs() {
const activeTab = Number(new URLSearchParams(search).get('tab')) ?? RSTabsList.CARD; const activeTab = Number(new URLSearchParams(search).get('tab')) ?? RSTabsList.CARD;
const cstQuery = new URLSearchParams(search).get('active'); const cstQuery = new URLSearchParams(search).get('active');
setActiveTab(activeTab); setActiveTab(activeTab);
setActiveID(cstQuery ?? ((schema && schema?.items.length > 0) ? schema.items[0].id : undefined)) setActiveID(Number(cstQuery) ?? ((schema && schema?.items.length > 0) ? schema.items[0].id : undefined))
}, [search, setActiveTab, setActiveID, schema]); }, [search, setActiveTab, setActiveID, schema]);
function onSelectTab(index: number) { function onSelectTab(index: number) {
@ -78,7 +78,7 @@ function RSTabs() {
} }
const navigateTo = useCallback( const navigateTo = useCallback(
(tab: RSTabsList, activeID?: string) => { (tab: RSTabsList, activeID?: number) => {
if (activeID) { if (activeID) {
navigate(`/rsforms/${schema!.id}?tab=${tab}&active=${activeID}`, { navigate(`/rsforms/${schema!.id}?tab=${tab}&active=${activeID}`, {
replace: tab === activeTab && tab !== RSTabsList.CST_EDIT replace: tab === activeTab && tab !== RSTabsList.CST_EDIT
@ -123,7 +123,7 @@ function RSTabs() {
}, [handleCreateCst]); }, [handleCreateCst]);
const handleDeleteCst = useCallback( const handleDeleteCst = useCallback(
(deleted: string[]) => { (deleted: number[]) => {
if (!schema) { if (!schema) {
return; return;
} }
@ -148,9 +148,9 @@ function RSTabs() {
}, [afterDelete, cstDelete, schema, activeID, activeTab, navigateTo]); }, [afterDelete, cstDelete, schema, activeID, activeTab, navigateTo]);
const promptDeleteCst = useCallback( const promptDeleteCst = useCallback(
(selected: string[], callback?: (items: string[]) => void) => { (selected: number[], callback?: (items: number[]) => void) => {
setAfterDelete(() => ( setAfterDelete(() => (
(items: string[]) => { (items: number[]) => {
if (callback) callback(items); if (callback) callback(items);
})); }));
setToBeDeleted(selected); setToBeDeleted(selected);
@ -165,7 +165,7 @@ function RSTabs() {
}, []); }, []);
const onOpenCst = useCallback( const onOpenCst = useCallback(
(cstID: string) => { (cstID: number) => {
navigateTo(RSTabsList.CST_EDIT, cstID) navigateTo(RSTabsList.CST_EDIT, cstID)
}, [navigateTo]); }, [navigateTo]);

View File

@ -17,12 +17,12 @@ const LOCAL_NAVIGATION_H = '2.6rem';
interface ViewSideConstituentsProps { interface ViewSideConstituentsProps {
expression: string expression: string
baseHeight: string baseHeight: string
activeID?: string activeID?: number
onOpenEdit: (cstID: string) => void onOpenEdit: (cstID: number) => void
} }
function isMockCst(cst: IConstituenta) { function isMockCst(cst: IConstituenta) {
return cst.id[0] === '-' return cst.id <= 0;
} }
function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }: ViewSideConstituentsProps) { function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }: ViewSideConstituentsProps) {
@ -49,7 +49,7 @@ function ViewSideConstituents({ expression, baseHeight, activeID, onOpenEdit }:
const diff = Array.from(aliases).filter(name => !names.includes(name)); const diff = Array.from(aliases).filter(name => !names.includes(name));
if (diff.length > 0) { if (diff.length > 0) {
diff.forEach( diff.forEach(
(alias, index) => filtered.push(getMockConstituenta(`-${index}`, alias, CstType.BASE, 'Конституента отсутствует'))); (alias, index) => filtered.push(getMockConstituenta(-index, alias, CstType.BASE, 'Конституента отсутствует')));
} }
} else if (!activeID) { } else if (!activeID) {
filtered = schema.items filtered = schema.items

View File

@ -8,94 +8,94 @@ describe('Testing Graph constuction', () => {
test('adding edges should create nodes', () => { test('adding edges should create nodes', () => {
const graph = new Graph(); const graph = new Graph();
graph.addEdge('13', '37'); graph.addEdge(13, 37);
expect([... graph.nodes.keys()]).toStrictEqual(['13', '37']); expect([... graph.nodes.keys()]).toStrictEqual([13, 37]);
graph.addEdge('13', '38'); graph.addEdge(13, 38);
expect([... graph.nodes.keys()]).toStrictEqual(['13', '37', '38']); expect([... graph.nodes.keys()]).toStrictEqual([13, 37, 38]);
}); });
test('creating from array', () => { test('creating from array', () => {
const graph = new Graph([['1', '2'], ['3'], ['4', '1']]); const graph = new Graph([[1, 2], [3], [4, 1]]);
expect([... graph.nodes.keys()]).toStrictEqual(['1', '2', '3', '4']); expect([... graph.nodes.keys()]).toStrictEqual([1, 2, 3, 4]);
expect([... graph.nodes.get('1')!.outputs]).toStrictEqual(['2']); expect([... graph.nodes.get(1)!.outputs]).toStrictEqual([2]);
}); });
test('cloning', () => { test('cloning', () => {
const graph = new Graph([['1', '2'], ['3'], ['4', '1']]); const graph = new Graph([[1, 2], [3], [4, 1]]);
const clone = graph.clone(); const clone = graph.clone();
expect([... graph.nodes.keys()]).toStrictEqual([... clone.nodes.keys()]); expect([... graph.nodes.keys()]).toStrictEqual([... clone.nodes.keys()]);
expect([... graph.nodes.values()]).toStrictEqual([... clone.nodes.values()]); expect([... graph.nodes.values()]).toStrictEqual([... clone.nodes.values()]);
clone.removeNode('3'); clone.removeNode(3);
expect(clone.nodes.get('3')).toBeUndefined(); expect(clone.nodes.get(3)).toBeUndefined();
expect(graph.nodes.get('3')).not.toBeUndefined(); expect(graph.nodes.get(3)).not.toBeUndefined();
}); });
}); });
describe('Testing Graph editing', () => { describe('Testing Graph editing', () => {
test('removing edges should not remove nodes', () => { test('removing edges should not remove nodes', () => {
const graph = new Graph([['1', '2'], ['3'], ['4', '1']]); const graph = new Graph([[1, 2], [3], [4, 1]]);
expect(graph.hasEdge('4', '1')).toBeTruthy(); expect(graph.hasEdge(4, 1)).toBeTruthy();
graph.removeEdge('5', '0'); graph.removeEdge(5, 0);
graph.removeEdge('4', '1'); graph.removeEdge(4, 1);
expect([... graph.nodes.keys()]).toStrictEqual(['1', '2', '3', '4']); expect([... graph.nodes.keys()]).toStrictEqual([1, 2, 3, 4]);
expect(graph.hasEdge('4', '1')).toBeFalsy(); expect(graph.hasEdge(4, 1)).toBeFalsy();
}); });
test('folding node redirectes edges', () => { test('folding node redirectes edges', () => {
const graph = new Graph([['1', '3'], ['2', '3'], ['3', '4'], ['3', '5'], ['3', '3']]); const graph = new Graph([[1, 3], [2, 3], [3, 4], [3, 5], [3, 3]]);
graph.foldNode('3'); graph.foldNode(3);
expect(graph.hasNode('3')).toBeFalsy(); expect(graph.hasNode(3)).toBeFalsy();
expect(graph.hasEdge('1', '4')).toBeTruthy(); expect(graph.hasEdge(1, 4)).toBeTruthy();
expect(graph.hasEdge('1', '5')).toBeTruthy(); expect(graph.hasEdge(1, 5)).toBeTruthy();
expect(graph.hasEdge('2', '4')).toBeTruthy(); expect(graph.hasEdge(2, 4)).toBeTruthy();
expect(graph.hasEdge('2', '5')).toBeTruthy(); expect(graph.hasEdge(2, 5)).toBeTruthy();
}); });
test('removing isolated nodes', () => { test('removing isolated nodes', () => {
const graph = new Graph([['9', '1'], ['9', '2'], ['2', '1'], ['4', '3'], ['5', '9'], ['7'], ['8']]); const graph = new Graph([[9, 1], [9, 2], [2, 1], [4, 3], [5, 9], [7], [8]]);
graph.removeIsolated() graph.removeIsolated()
expect([... graph.nodes.keys()]).toStrictEqual(['9', '1', '2', '4', '3', '5']); expect([... graph.nodes.keys()]).toStrictEqual([9, 1, 2, 4, 3, 5]);
}); });
test('transitive reduction', () => { test('transitive reduction', () => {
const graph = new Graph([['1', '3'], ['1', '2'], ['2', '3']]); const graph = new Graph([[1, 3], [1, 2], [2, 3]]);
graph.transitiveReduction(); graph.transitiveReduction();
expect(graph.hasEdge('1', '2')).toBeTruthy(); expect(graph.hasEdge(1, 2)).toBeTruthy();
expect(graph.hasEdge('2', '3')).toBeTruthy(); expect(graph.hasEdge(2, 3)).toBeTruthy();
expect(graph.hasEdge('1', '3')).toBeFalsy(); expect(graph.hasEdge(1, 3)).toBeFalsy();
}); });
}); });
describe('Testing Graph sort', () => { describe('Testing Graph sort', () => {
test('topological order', () => { test('topological order', () => {
const graph = new Graph([['9', '1'], ['9', '2'], ['2', '1'], ['4', '3'], ['5', '9']]); const graph = new Graph([[9, 1], [9, 2], [2, 1], [4, 3], [5, 9]]);
expect(graph.tolopogicalOrder()).toStrictEqual(['5', '4', '3', '9', '2', '1']); expect(graph.tolopogicalOrder()).toStrictEqual([5, 4, 3, 9, 2, 1]);
}); });
}); });
describe('Testing Graph queries', () => { describe('Testing Graph queries', () => {
test('expand outputs', () => { test('expand outputs', () => {
const graph = new Graph([['1', '2'], ['2', '3'], ['2', '5'], ['5', '6'], ['6', '1'], ['7']]); const graph = new Graph([[1, 2], [2, 3], [2, 5], [5, 6], [6, 1], [7]]);
expect(graph.expandOutputs([])).toStrictEqual([]); expect(graph.expandOutputs([])).toStrictEqual([]);
expect(graph.expandOutputs(['3'])).toStrictEqual([]); expect(graph.expandOutputs([3])).toStrictEqual([]);
expect(graph.expandOutputs(['7'])).toStrictEqual([]); expect(graph.expandOutputs([7])).toStrictEqual([]);
expect(graph.expandOutputs(['2', '5'])).toStrictEqual(['3', '6', '1']); expect(graph.expandOutputs([2, 5])).toStrictEqual([3, 6, 1]);
}); });
test('expand into unique array', () => { test('expand into unique array', () => {
const graph = new Graph([['1', '2'], ['1', '3'], ['2', '5'], ['3', '5']]); const graph = new Graph([[1, 2], [1, 3], [2, 5], [3, 5]]);
expect(graph.expandOutputs(['1'])).toStrictEqual(['2', '3' , '5']); expect(graph.expandOutputs([1])).toStrictEqual([2, 3 , 5]);
}); });
test('expand inputs', () => { test('expand inputs', () => {
const graph = new Graph([['1', '2'], ['2', '3'], ['2', '5'], ['5', '6'], ['6', '1'], ['7']]); const graph = new Graph([[1, 2], [2, 3], [2, 5], [5, 6], [6, 1], [7]]);
expect(graph.expandInputs([])).toStrictEqual([]); expect(graph.expandInputs([])).toStrictEqual([]);
expect(graph.expandInputs(['7'])).toStrictEqual([]); expect(graph.expandInputs([7])).toStrictEqual([]);
expect(graph.expandInputs(['6'])).toStrictEqual(['5', '2', '1']); expect(graph.expandInputs([6])).toStrictEqual([5, 2, 1]);
}); });
}); });

View File

@ -1,10 +1,10 @@
// ======== ID based fast Graph implementation ============= // ======== ID based fast Graph implementation =============
export class GraphNode { export class GraphNode {
id: string; id: number;
outputs: string[]; outputs: number[];
inputs: string[]; inputs: number[];
constructor(id: string) { constructor(id: number) {
this.id = id; this.id = id;
this.outputs = []; this.outputs = [];
this.inputs = []; this.inputs = [];
@ -17,29 +17,29 @@ export class GraphNode {
return result; return result;
} }
addOutput(node: string): void { addOutput(node: number): void {
this.outputs.push(node); this.outputs.push(node);
} }
addInput(node: string): void { addInput(node: number): void {
this.inputs.push(node); this.inputs.push(node);
} }
removeInput(target: string): string | null { removeInput(target: number): number | null {
const index = this.inputs.findIndex(node => node === target); const index = this.inputs.findIndex(node => node === target);
return index > -1 ? this.inputs.splice(index, 1)[0] : null; return index > -1 ? this.inputs.splice(index, 1)[0] : null;
} }
removeOutput(target: string): string | null { removeOutput(target: number): number | null {
const index = this.outputs.findIndex(node => node === target); const index = this.outputs.findIndex(node => node === target);
return index > -1 ? this.outputs.splice(index, 1)[0] : null; return index > -1 ? this.outputs.splice(index, 1)[0] : null;
} }
} }
export class Graph { export class Graph {
nodes: Map<string, GraphNode> = new Map(); nodes: Map<number, GraphNode> = new Map();
constructor(arr?: string[][]) { constructor(arr?: number[][]) {
if (!arr) { if (!arr) {
return; return;
} }
@ -58,7 +58,7 @@ export class Graph {
return result; return result;
} }
addNode(target: string): GraphNode { addNode(target: number): GraphNode {
let node = this.nodes.get(target); let node = this.nodes.get(target);
if (!node) { if (!node) {
node = new GraphNode(target); node = new GraphNode(target);
@ -67,11 +67,11 @@ export class Graph {
return node; return node;
} }
hasNode(target: string): boolean { hasNode(target: number): boolean {
return !!this.nodes.get(target); return !!this.nodes.get(target);
} }
removeNode(target: string): GraphNode | null { removeNode(target: number): GraphNode | null {
const nodeToRemove = this.nodes.get(target); const nodeToRemove = this.nodes.get(target);
if (!nodeToRemove) { if (!nodeToRemove) {
return null; return null;
@ -84,7 +84,7 @@ export class Graph {
return nodeToRemove; return nodeToRemove;
} }
foldNode(target: string): GraphNode | null { foldNode(target: number): GraphNode | null {
const nodeToRemove = this.nodes.get(target); const nodeToRemove = this.nodes.get(target);
if (!nodeToRemove) { if (!nodeToRemove) {
return null; return null;
@ -107,14 +107,14 @@ export class Graph {
return result; return result;
} }
addEdge(source: string, destination: string): void { addEdge(source: number, destination: number): void {
const sourceNode = this.addNode(source); const sourceNode = this.addNode(source);
const destinationNode = this.addNode(destination); const destinationNode = this.addNode(destination);
sourceNode.addOutput(destinationNode.id); sourceNode.addOutput(destinationNode.id);
destinationNode.addInput(sourceNode.id); destinationNode.addInput(sourceNode.id);
} }
removeEdge(source: string, destination: string): void { removeEdge(source: number, destination: number): void {
const sourceNode = this.nodes.get(source); const sourceNode = this.nodes.get(source);
const destinationNode = this.nodes.get(destination); const destinationNode = this.nodes.get(destination);
if (sourceNode && destinationNode) { if (sourceNode && destinationNode) {
@ -123,7 +123,7 @@ export class Graph {
} }
} }
hasEdge(source: string, destination: string): boolean { hasEdge(source: number, destination: number): boolean {
const sourceNode = this.nodes.get(source); const sourceNode = this.nodes.get(source);
if (!sourceNode) { if (!sourceNode) {
return false; return false;
@ -131,9 +131,9 @@ export class Graph {
return !!sourceNode.outputs.find(id => id === destination); return !!sourceNode.outputs.find(id => id === destination);
} }
expandOutputs(origin: string[]): string[] { expandOutputs(origin: number[]): number[] {
const result: string[] = []; const result: number[] = [];
const marked = new Map<string, boolean>(); const marked = new Map<number, boolean>();
origin.forEach(id => marked.set(id, true)); origin.forEach(id => marked.set(id, true));
origin.forEach(id => { origin.forEach(id => {
const node = this.nodes.get(id); const node = this.nodes.get(id);
@ -161,9 +161,9 @@ export class Graph {
return result; return result;
} }
expandInputs(origin: string[]): string[] { expandInputs(origin: number[]): number[] {
const result: string[] = []; const result: number[] = [];
const marked = new Map<string, boolean>(); const marked = new Map<number, boolean>();
origin.forEach(id => marked.set(id, true)); origin.forEach(id => marked.set(id, true));
origin.forEach(id => { origin.forEach(id => {
const node = this.nodes.get(id); const node = this.nodes.get(id);
@ -191,10 +191,10 @@ export class Graph {
return result; return result;
} }
tolopogicalOrder(): string[] { tolopogicalOrder(): number[] {
const result: string[] = []; const result: number[] = [];
const marked = new Map<string, boolean>(); const marked = new Map<number, boolean>();
const toVisit: string[] = []; const toVisit: number[] = [];
this.nodes.forEach(node => { this.nodes.forEach(node => {
if (marked.get(node.id)) { if (marked.get(node.id)) {
return; return;
@ -225,12 +225,12 @@ export class Graph {
transitiveReduction() { transitiveReduction() {
const order = this.tolopogicalOrder(); const order = this.tolopogicalOrder();
const marked = new Map<string, boolean>(); const marked = new Map<number, boolean>();
order.forEach(nodeID => { order.forEach(nodeID => {
if (marked.get(nodeID)) { if (marked.get(nodeID)) {
return; return;
} }
const stack: {id: string, parents: string[]}[] = []; const stack: {id: number, parents: number[]}[] = [];
stack.push({id: nodeID, parents: []}); stack.push({id: nodeID, parents: []});
while (stack.length > 0) { while (stack.length > 0) {
const item = stack.splice(0, 1)[0]; const item = stack.splice(0, 1)[0];

View File

@ -196,7 +196,7 @@ export function postNewConstituenta(schema: string, request: FrontExchange<ICstC
export function patchDeleteConstituenta(schema: string, request: FrontExchange<IConstituentaList, IRSFormData>) { export function patchDeleteConstituenta(schema: string, request: FrontExchange<IConstituentaList, IRSFormData>) {
AxiosPatch({ AxiosPatch({
title: `Delete Constituents for RSForm id=${schema}: ${request.data.items.map(item => item.id).join(' ')}`, title: `Delete Constituents for RSForm id=${schema}: ${request.data.items.map(item => String(item.id)).join(' ')}`,
endpoint: `/api/rsforms/${schema}/cst-multidelete/`, endpoint: `/api/rsforms/${schema}/cst-multidelete/`,
request: request request: request
}); });

View File

@ -152,6 +152,11 @@ export enum RSErrorType {
typesNotCompatible = 0x8825, // Типы не совместимы в данном контексте typesNotCompatible = 0x8825, // Типы не совместимы в данном контексте
orderingNotSupported = 0x8826, // Для данного типа не поддерживается порядок элементов orderingNotSupported = 0x8826, // Для данного типа не поддерживается порядок элементов
// !!!! Добавлены по сравнению с ConceptCore !!!!!
globalNonemptyBase = 0x8827, // Непустое выражение базисного/константного множества
globalUnexpectedType = 0x8828, // Типизация выражения не соответствует типу конституенты
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
globalNoValue = 0x8840, // Используется неинтерпретируемый глобальный идентификатор globalNoValue = 0x8840, // Используется неинтерпретируемый глобальный идентификатор
invalidPropertyUsage = 0x8841, // Использование свойства в качестве значения invalidPropertyUsage = 0x8841, // Использование свойства в качестве значения
globalMissingAST = 0x8842, // Не удалось получить дерево разбора для глобального идентификатора globalMissingAST = 0x8842, // Не удалось получить дерево разбора для глобального идентификатора

View File

@ -109,7 +109,7 @@ export enum CstClass {
} }
export interface IConstituenta { export interface IConstituenta {
id: string id: number
alias: string alias: string
cstType: CstType cstType: CstType
convention: string convention: string
@ -138,8 +138,8 @@ export interface IConstituenta {
} }
export interface IConstituentaMeta { export interface IConstituentaMeta {
id: string id: number
schema: string schema: number
order: number order: number
alias: string alias: string
convention: string convention: string
@ -158,7 +158,7 @@ export interface IConstituentaList {
export interface ICstCreateData export interface ICstCreateData
extends Pick<IConstituentaMeta, 'alias' | 'cst_type' | 'definition_raw' | 'term_raw' | 'convention' | 'definition_formal' > { extends Pick<IConstituentaMeta, 'alias' | 'cst_type' | 'definition_raw' | 'term_raw' | 'convention' | 'definition_formal' > {
insert_after: string | null insert_after: number | null
} }
export interface ICstMovetoData extends IConstituentaList { export interface ICstMovetoData extends IConstituentaList {
@ -196,7 +196,7 @@ export interface IRSFormStats {
} }
export interface IRSForm { export interface IRSForm {
id: string id: number
title: string title: string
alias: string alias: string
comment: string comment: string
@ -368,9 +368,7 @@ export function LoadRSFormData(schema: IRSFormData): IRSForm {
count_theorem: result.items.reduce( count_theorem: result.items.reduce(
(sum, cst) => sum + (cst.cstType === CstType.THEOREM ? 1 : 0), 0) (sum, cst) => sum + (cst.cstType === CstType.THEOREM ? 1 : 0), 0)
} }
result.id = String(result.id)
result.items.forEach(cst => { result.items.forEach(cst => {
cst.id = String(cst.id)
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass); cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
cst.isTemplate = inferTemplate(cst.definition.formal); cst.isTemplate = inferTemplate(cst.definition.formal);
cst.cstClass = inferClass(cst.cstType, cst.isTemplate); cst.cstClass = inferClass(cst.cstType, cst.isTemplate);
@ -416,11 +414,11 @@ export function matchRSFormMeta(query: string, target: IRSFormMeta) {
} }
} }
export function applyGraphFilter(schema: IRSForm, start: string, mode: DependencyMode): IConstituenta[] { export function applyGraphFilter(schema: IRSForm, start: number, mode: DependencyMode): IConstituenta[] {
if (mode === DependencyMode.ALL) { if (mode === DependencyMode.ALL) {
return schema.items; return schema.items;
} }
let ids: string[] | undefined = undefined let ids: number[] | undefined = undefined
switch (mode) { switch (mode) {
case DependencyMode.OUTPUTS: { ids = schema.graph.nodes.get(start)?.outputs; break; } case DependencyMode.OUTPUTS: { ids = schema.graph.nodes.get(start)?.outputs; break; }
case DependencyMode.INPUTS: { ids = schema.graph.nodes.get(start)?.inputs; break; } case DependencyMode.INPUTS: { ids = schema.graph.nodes.get(start)?.inputs; break; }

View File

@ -407,7 +407,7 @@ export function createAliasFor(type: CstType, schema: IRSForm): string {
return `${prefix}${index}`; return `${prefix}${index}`;
} }
export function getMockConstituenta(id: string, alias: string, type: CstType, comment: string): IConstituenta { export function getMockConstituenta(id: number, alias: string, type: CstType, comment: string): IConstituenta {
return { return {
id: id, id: id,
alias: alias, alias: alias,
@ -568,6 +568,10 @@ export function getRSErrorMessage(error: IRSErrorDescription): string {
return `Не удалось получить дерево разбора для глобального идентификатора: ${error.params[0]}`; return `Не удалось получить дерево разбора для глобального идентификатора: ${error.params[0]}`;
case RSErrorType.globalFuncNoInterpretation: case RSErrorType.globalFuncNoInterpretation:
return `Функция не интерпретируется для данных аргументов`; return `Функция не интерпретируется для данных аргументов`;
case RSErrorType.globalNonemptyBase:
return `Непустое выражение базисного/константного множества`;
case RSErrorType.globalUnexpectedType:
return `Типизация выражения не соответствует типу конституенты`;
} }
return 'UNKNOWN ERROR'; return 'UNKNOWN ERROR';
} }