mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
Revert changing ID types and minor ui fixes
This commit is contained in:
parent
503ad5d0dc
commit
b85f057c03
|
@ -14,8 +14,8 @@ interface ILibraryContext {
|
|||
|
||||
filter: (params: ILibraryFilter) => IRSFormMeta[]
|
||||
createSchema: (data: IRSFormCreateData, callback?: DataCallback<IRSFormMeta>) => void
|
||||
cloneSchema: (target: string, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => void
|
||||
destroySchema: (target: string, callback?: () => void) => void
|
||||
cloneSchema: (target: number, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => void
|
||||
destroySchema: (target: number, callback?: () => void) => void
|
||||
}
|
||||
|
||||
const LibraryContext = createContext<ILibraryContext | null>(null)
|
||||
|
@ -62,8 +62,8 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
getLibrary({
|
||||
setLoading: setLoading,
|
||||
showError: true,
|
||||
onError: (error) => setError(error),
|
||||
onSuccess: newData => {
|
||||
onError: error => setError(error),
|
||||
onSuccess: newData => {
|
||||
setItems(newData);
|
||||
if (callback) callback();
|
||||
}
|
||||
|
@ -90,9 +90,9 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
}, [reload]);
|
||||
|
||||
const destroySchema = useCallback(
|
||||
(target: string, callback?: () => void) => {
|
||||
(target: number, callback?: () => void) => {
|
||||
setError(undefined)
|
||||
deleteRSForm(target, {
|
||||
deleteRSForm(String(target), {
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: error => setError(error),
|
||||
|
@ -103,12 +103,12 @@ export const LibraryState = ({ children }: LibraryStateProps) => {
|
|||
}, [setError, reload]);
|
||||
|
||||
const cloneSchema = useCallback(
|
||||
(target: string, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => {
|
||||
(target: number, data: IRSFormCreateData, callback: DataCallback<IRSFormData>) => {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
setError(undefined)
|
||||
postCloneRSForm(target, {
|
||||
postCloneRSForm(String(target), {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
|
|
|
@ -205,7 +205,7 @@ export const RSFormState = ({ schemaID, children }: RSFormStateProps) => {
|
|||
const cstUpdate = useCallback(
|
||||
(data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => {
|
||||
setError(undefined)
|
||||
patchConstituenta(data.id, {
|
||||
patchConstituenta(String(data.id), {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
|
|
|
@ -2,7 +2,29 @@ import { useCallback, useState } from 'react'
|
|||
|
||||
import { type ErrorInfo } from '../components/BackendError';
|
||||
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 }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
@ -11,16 +33,38 @@ function useCheckExpression({ schema }: { schema?: IRSForm }) {
|
|||
|
||||
const resetParse = useCallback(() => { setParseData(undefined); }, []);
|
||||
|
||||
function checkExpression(expression: string, onSuccess?: DataCallback<IExpressionParse>) {
|
||||
function checkExpression(expression: string, activeCst?: IConstituenta, onSuccess?: DataCallback<IExpressionParse>) {
|
||||
setError(undefined);
|
||||
postCheckExpression(schema!.id, {
|
||||
postCheckExpression(String(schema!.id), {
|
||||
data: { expression: expression },
|
||||
showError: true,
|
||||
setLoading,
|
||||
onError: error => { setError(error); },
|
||||
onSuccess: newData => {
|
||||
setParseData(newData);
|
||||
if (onSuccess) onSuccess(newData);
|
||||
onSuccess: parse => {
|
||||
if (activeCst && parse.parseResult) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,15 +7,15 @@ import { getCstLabel } from '../../utils/staticUI';
|
|||
|
||||
interface DlgDeleteCstProps {
|
||||
hideWindow: () => void
|
||||
selected: string[]
|
||||
onDelete: (items: string[]) => void
|
||||
selected: number[]
|
||||
onDelete: (items: number[]) => void
|
||||
}
|
||||
|
||||
function DlgDeleteCst({ hideWindow, selected, onDelete }: DlgDeleteCstProps) {
|
||||
const { schema } = useRSForm();
|
||||
|
||||
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() {
|
||||
hideWindow();
|
||||
|
|
|
@ -18,11 +18,11 @@ import ViewSideConstituents from './elements/ViewSideConstituents';
|
|||
const UNFOLDED_HEIGHT = '59.1rem';
|
||||
|
||||
interface EditorConstituentaProps {
|
||||
activeID?: string
|
||||
onOpenEdit: (cstID: string) => void
|
||||
activeID?: number
|
||||
onOpenEdit: (cstID: number) => void
|
||||
onShowAST: (expression: string, ast: SyntaxTree) => 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) {
|
||||
|
|
|
@ -14,15 +14,15 @@ import { CstType, IConstituenta, ICstCreateData, ICstMovetoData } from '../../ut
|
|||
import { getCstTypePrefix, getCstTypeShortcut, getCstTypificationLabel, mapStatusInfo } from '../../utils/staticUI';
|
||||
|
||||
interface EditorItemsProps {
|
||||
onOpenEdit: (cstID: string) => void
|
||||
onOpenEdit: (cstID: number) => 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) {
|
||||
const { schema, isEditable, cstMoveTo, resetAliases } = useRSForm();
|
||||
const { noNavigation } = useConceptTheme();
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const [selected, setSelected] = useState<number[]>([]);
|
||||
const nothingSelected = useMemo(() => selected.length === 0, [selected]);
|
||||
|
||||
const [toggledClearRows, setToggledClearRows] = useState(false);
|
||||
|
|
|
@ -60,7 +60,7 @@ function EditorRSExpression({
|
|||
}
|
||||
const prefix = getCstExpressionPrefix(activeCst);
|
||||
const expression = prefix + value;
|
||||
checkExpression(expression, parse => {
|
||||
checkExpression(expression, activeCst, parse => {
|
||||
if (parse.errors.length > 0) {
|
||||
onShowError(parse.errors[0]);
|
||||
} else {
|
||||
|
@ -81,7 +81,8 @@ function EditorRSExpression({
|
|||
return;
|
||||
}
|
||||
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({
|
||||
selection: {
|
||||
anchor: errorPosition,
|
||||
|
|
|
@ -56,9 +56,9 @@ export interface GraphEditorParams {
|
|||
}
|
||||
|
||||
interface EditorTermGraphProps {
|
||||
onOpenEdit: (cstID: string) => void
|
||||
onOpenEdit: (cstID: number) => 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) {
|
||||
|
@ -83,16 +83,16 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
const [ allowTheorem, setAllowTheorem ] = useLocalStorage('graph_allow_theorem', true);
|
||||
|
||||
const [ filtered, setFiltered ] = useState<Graph>(new Graph());
|
||||
const [ dismissed, setDismissed ] = useState<string[]>([]);
|
||||
const [ selectedDismissed, setSelectedDismissed ] = useState<string[]>([]);
|
||||
const [ dismissed, setDismissed ] = useState<number[]>([]);
|
||||
const [ selectedDismissed, setSelectedDismissed ] = useState<number[]>([]);
|
||||
const graphRef = useRef<GraphCanvasRef | null>(null);
|
||||
const [showOptions, setShowOptions] = useState(false);
|
||||
const [toggleUpdate, setToggleUpdate] = useState(false);
|
||||
|
||||
const [hoverID, setHoverID] = useState<string | undefined>(undefined);
|
||||
const [hoverID, setHoverID] = useState<number | undefined>(undefined);
|
||||
const hoverCst = useMemo(
|
||||
() => {
|
||||
return schema?.items.find(cst => String(cst.id) === hoverID);
|
||||
return schema?.items.find(cst => cst.id === hoverID);
|
||||
}, [schema?.items, hoverID]);
|
||||
|
||||
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 => {
|
||||
if (!graph.nodes.has(cst.id)) {
|
||||
newDismissed.push(cst.id);
|
||||
|
@ -149,7 +149,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
setHoverID(undefined);
|
||||
}, [schema, noHermits, noTransitive, noTemplates, allowedTypes, toggleUpdate]);
|
||||
|
||||
function toggleDismissed(cstID: string) {
|
||||
function toggleDismissed(cstID: number) {
|
||||
setSelectedDismissed(prev => {
|
||||
const index = prev.findIndex(id => cstID === id);
|
||||
if (index !== -1) {
|
||||
|
@ -171,7 +171,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
const cst = schema.items.find(cst => cst.id === node.id);
|
||||
if (cst) {
|
||||
result.push({
|
||||
id: node.id,
|
||||
id: String(node.id),
|
||||
fill: getCstNodeColor(cst, coloringScheme, darkMode),
|
||||
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 => {
|
||||
result.push({
|
||||
id: String(edgeID),
|
||||
source: source.id,
|
||||
target: target
|
||||
source: String(source.id),
|
||||
target: String(target)
|
||||
});
|
||||
edgeID += 1;
|
||||
});
|
||||
|
@ -216,9 +216,9 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
focusOnSelect: false
|
||||
});
|
||||
|
||||
const allSelected: string[] = useMemo(
|
||||
const allSelected: number[] = useMemo(
|
||||
() => {
|
||||
return [ ... selectedDismissed, ... selections];
|
||||
return [ ... selectedDismissed, ... selections.map(id => Number(id))];
|
||||
}, [selectedDismissed, selections]);
|
||||
const nothingSelected = useMemo(() => allSelected.length === 0, [allSelected]);
|
||||
|
||||
|
@ -230,7 +230,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
|
||||
const handleHoverIn = useCallback(
|
||||
(node: GraphNode) => {
|
||||
setHoverID(node.id);
|
||||
setHoverID(Number(node.id));
|
||||
if (onNodePointerOver) onNodePointerOver(node);
|
||||
}, [onNodePointerOver]);
|
||||
|
||||
|
@ -243,7 +243,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
const handleNodeClick = useCallback(
|
||||
(node: GraphNode) => {
|
||||
if (selections.includes(node.id)) {
|
||||
onOpenEdit(node.id);
|
||||
onOpenEdit(Number(node.id));
|
||||
return;
|
||||
}
|
||||
if (onNodeClick) onNodeClick(node);
|
||||
|
@ -346,7 +346,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
}, [noNavigation]);
|
||||
|
||||
const dismissedStyle = useCallback(
|
||||
(cstID: string) => {
|
||||
(cstID: number) => {
|
||||
return selectedDismissed.includes(cstID) ? {outlineWidth: '2px', outlineStyle: 'solid'}: {};
|
||||
}, [selectedDismissed]);
|
||||
|
||||
|
@ -358,7 +358,7 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
initial={getOptions()}
|
||||
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 &&
|
||||
<div className='relative'>
|
||||
<InfoConstituenta
|
||||
|
@ -408,10 +408,10 @@ function EditorTermGraph({ onOpenEdit, onCreateCst, onDeleteCst }: EditorTermGra
|
|||
|
||||
</div>
|
||||
<ConceptSelect
|
||||
className='mt-1 w-fit'
|
||||
className='w-full mt-1'
|
||||
options={GraphLayoutSelector}
|
||||
searchable={false}
|
||||
placeholder='Выберите тип'
|
||||
placeholder='Способ расположения'
|
||||
values={layout ? [{ value: layout, label: mapLayoutLabels.get(layout) }] : []}
|
||||
onChange={data => { setLayout(data.length > 0 ? data[0].value : GraphLayoutSelector[0].value); }}
|
||||
/>
|
||||
|
|
|
@ -40,7 +40,7 @@ function RSTabs() {
|
|||
const { destroySchema } = useLibrary();
|
||||
|
||||
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 [showClone, setShowClone] = useState(false);
|
||||
|
@ -49,8 +49,8 @@ function RSTabs() {
|
|||
const [expression, setExpression] = useState('');
|
||||
const [showAST, setShowAST] = useState(false);
|
||||
|
||||
const [afterDelete, setAfterDelete] = useState<((items: string[]) => void) | undefined>(undefined);
|
||||
const [toBeDeleted, setToBeDeleted] = useState<string[]>([]);
|
||||
const [afterDelete, setAfterDelete] = useState<((items: number[]) => void) | undefined>(undefined);
|
||||
const [toBeDeleted, setToBeDeleted] = useState<number[]>([]);
|
||||
const [showDeleteCst, setShowDeleteCst] = useState(false);
|
||||
|
||||
const [createInitialData, setCreateInitialData] = useState<ICstCreateData>();
|
||||
|
@ -70,7 +70,7 @@ function RSTabs() {
|
|||
const activeTab = Number(new URLSearchParams(search).get('tab')) ?? RSTabsList.CARD;
|
||||
const cstQuery = new URLSearchParams(search).get('active');
|
||||
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]);
|
||||
|
||||
function onSelectTab(index: number) {
|
||||
|
@ -78,7 +78,7 @@ function RSTabs() {
|
|||
}
|
||||
|
||||
const navigateTo = useCallback(
|
||||
(tab: RSTabsList, activeID?: string) => {
|
||||
(tab: RSTabsList, activeID?: number) => {
|
||||
if (activeID) {
|
||||
navigate(`/rsforms/${schema!.id}?tab=${tab}&active=${activeID}`, {
|
||||
replace: tab === activeTab && tab !== RSTabsList.CST_EDIT
|
||||
|
@ -123,7 +123,7 @@ function RSTabs() {
|
|||
}, [handleCreateCst]);
|
||||
|
||||
const handleDeleteCst = useCallback(
|
||||
(deleted: string[]) => {
|
||||
(deleted: number[]) => {
|
||||
if (!schema) {
|
||||
return;
|
||||
}
|
||||
|
@ -148,9 +148,9 @@ function RSTabs() {
|
|||
}, [afterDelete, cstDelete, schema, activeID, activeTab, navigateTo]);
|
||||
|
||||
const promptDeleteCst = useCallback(
|
||||
(selected: string[], callback?: (items: string[]) => void) => {
|
||||
(selected: number[], callback?: (items: number[]) => void) => {
|
||||
setAfterDelete(() => (
|
||||
(items: string[]) => {
|
||||
(items: number[]) => {
|
||||
if (callback) callback(items);
|
||||
}));
|
||||
setToBeDeleted(selected);
|
||||
|
@ -165,7 +165,7 @@ function RSTabs() {
|
|||
}, []);
|
||||
|
||||
const onOpenCst = useCallback(
|
||||
(cstID: string) => {
|
||||
(cstID: number) => {
|
||||
navigateTo(RSTabsList.CST_EDIT, cstID)
|
||||
}, [navigateTo]);
|
||||
|
||||
|
|
|
@ -17,12 +17,12 @@ const LOCAL_NAVIGATION_H = '2.6rem';
|
|||
interface ViewSideConstituentsProps {
|
||||
expression: string
|
||||
baseHeight: string
|
||||
activeID?: string
|
||||
onOpenEdit: (cstID: string) => void
|
||||
activeID?: number
|
||||
onOpenEdit: (cstID: number) => void
|
||||
}
|
||||
|
||||
function isMockCst(cst: IConstituenta) {
|
||||
return cst.id[0] === '-'
|
||||
return cst.id <= 0;
|
||||
}
|
||||
|
||||
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));
|
||||
if (diff.length > 0) {
|
||||
diff.forEach(
|
||||
(alias, index) => filtered.push(getMockConstituenta(`-${index}`, alias, CstType.BASE, 'Конституента отсутствует')));
|
||||
(alias, index) => filtered.push(getMockConstituenta(-index, alias, CstType.BASE, 'Конституента отсутствует')));
|
||||
}
|
||||
} else if (!activeID) {
|
||||
filtered = schema.items
|
||||
|
|
|
@ -8,94 +8,94 @@ describe('Testing Graph constuction', () => {
|
|||
|
||||
test('adding edges should create nodes', () => {
|
||||
const graph = new Graph();
|
||||
graph.addEdge('13', '37');
|
||||
expect([... graph.nodes.keys()]).toStrictEqual(['13', '37']);
|
||||
graph.addEdge(13, 37);
|
||||
expect([... graph.nodes.keys()]).toStrictEqual([13, 37]);
|
||||
|
||||
graph.addEdge('13', '38');
|
||||
expect([... graph.nodes.keys()]).toStrictEqual(['13', '37', '38']);
|
||||
graph.addEdge(13, 38);
|
||||
expect([... graph.nodes.keys()]).toStrictEqual([13, 37, 38]);
|
||||
});
|
||||
|
||||
test('creating from array', () => {
|
||||
const graph = new Graph([['1', '2'], ['3'], ['4', '1']]);
|
||||
expect([... graph.nodes.keys()]).toStrictEqual(['1', '2', '3', '4']);
|
||||
expect([... graph.nodes.get('1')!.outputs]).toStrictEqual(['2']);
|
||||
const graph = new Graph([[1, 2], [3], [4, 1]]);
|
||||
expect([... graph.nodes.keys()]).toStrictEqual([1, 2, 3, 4]);
|
||||
expect([... graph.nodes.get(1)!.outputs]).toStrictEqual([2]);
|
||||
});
|
||||
|
||||
test('cloning', () => {
|
||||
const graph = new Graph([['1', '2'], ['3'], ['4', '1']]);
|
||||
const graph = new Graph([[1, 2], [3], [4, 1]]);
|
||||
const clone = graph.clone();
|
||||
expect([... graph.nodes.keys()]).toStrictEqual([... clone.nodes.keys()]);
|
||||
expect([... graph.nodes.values()]).toStrictEqual([... clone.nodes.values()]);
|
||||
|
||||
clone.removeNode('3');
|
||||
expect(clone.nodes.get('3')).toBeUndefined();
|
||||
expect(graph.nodes.get('3')).not.toBeUndefined();
|
||||
clone.removeNode(3);
|
||||
expect(clone.nodes.get(3)).toBeUndefined();
|
||||
expect(graph.nodes.get(3)).not.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Testing Graph editing', () => {
|
||||
test('removing edges should not remove nodes', () => {
|
||||
const graph = new Graph([['1', '2'], ['3'], ['4', '1']]);
|
||||
expect(graph.hasEdge('4', '1')).toBeTruthy();
|
||||
const graph = new Graph([[1, 2], [3], [4, 1]]);
|
||||
expect(graph.hasEdge(4, 1)).toBeTruthy();
|
||||
|
||||
graph.removeEdge('5', '0');
|
||||
graph.removeEdge('4', '1');
|
||||
graph.removeEdge(5, 0);
|
||||
graph.removeEdge(4, 1);
|
||||
|
||||
expect([... graph.nodes.keys()]).toStrictEqual(['1', '2', '3', '4']);
|
||||
expect(graph.hasEdge('4', '1')).toBeFalsy();
|
||||
expect([... graph.nodes.keys()]).toStrictEqual([1, 2, 3, 4]);
|
||||
expect(graph.hasEdge(4, 1)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('folding node redirectes edges', () => {
|
||||
const graph = new Graph([['1', '3'], ['2', '3'], ['3', '4'], ['3', '5'], ['3', '3']]);
|
||||
graph.foldNode('3');
|
||||
expect(graph.hasNode('3')).toBeFalsy();
|
||||
expect(graph.hasEdge('1', '4')).toBeTruthy();
|
||||
expect(graph.hasEdge('1', '5')).toBeTruthy();
|
||||
expect(graph.hasEdge('2', '4')).toBeTruthy();
|
||||
expect(graph.hasEdge('2', '5')).toBeTruthy();
|
||||
const graph = new Graph([[1, 3], [2, 3], [3, 4], [3, 5], [3, 3]]);
|
||||
graph.foldNode(3);
|
||||
expect(graph.hasNode(3)).toBeFalsy();
|
||||
expect(graph.hasEdge(1, 4)).toBeTruthy();
|
||||
expect(graph.hasEdge(1, 5)).toBeTruthy();
|
||||
expect(graph.hasEdge(2, 4)).toBeTruthy();
|
||||
expect(graph.hasEdge(2, 5)).toBeTruthy();
|
||||
});
|
||||
|
||||
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()
|
||||
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', () => {
|
||||
const graph = new Graph([['1', '3'], ['1', '2'], ['2', '3']]);
|
||||
const graph = new Graph([[1, 3], [1, 2], [2, 3]]);
|
||||
graph.transitiveReduction();
|
||||
expect(graph.hasEdge('1', '2')).toBeTruthy();
|
||||
expect(graph.hasEdge('2', '3')).toBeTruthy();
|
||||
expect(graph.hasEdge('1', '3')).toBeFalsy();
|
||||
expect(graph.hasEdge(1, 2)).toBeTruthy();
|
||||
expect(graph.hasEdge(2, 3)).toBeTruthy();
|
||||
expect(graph.hasEdge(1, 3)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Testing Graph sort', () => {
|
||||
test('topological order', () => {
|
||||
const graph = new Graph([['9', '1'], ['9', '2'], ['2', '1'], ['4', '3'], ['5', '9']]);
|
||||
expect(graph.tolopogicalOrder()).toStrictEqual(['5', '4', '3', '9', '2', '1']);
|
||||
const graph = new Graph([[9, 1], [9, 2], [2, 1], [4, 3], [5, 9]]);
|
||||
expect(graph.tolopogicalOrder()).toStrictEqual([5, 4, 3, 9, 2, 1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Testing Graph queries', () => {
|
||||
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(['3'])).toStrictEqual([]);
|
||||
expect(graph.expandOutputs(['7'])).toStrictEqual([]);
|
||||
expect(graph.expandOutputs(['2', '5'])).toStrictEqual(['3', '6', '1']);
|
||||
expect(graph.expandOutputs([3])).toStrictEqual([]);
|
||||
expect(graph.expandOutputs([7])).toStrictEqual([]);
|
||||
expect(graph.expandOutputs([2, 5])).toStrictEqual([3, 6, 1]);
|
||||
});
|
||||
|
||||
test('expand into unique array', () => {
|
||||
const graph = new Graph([['1', '2'], ['1', '3'], ['2', '5'], ['3', '5']]);
|
||||
expect(graph.expandOutputs(['1'])).toStrictEqual(['2', '3' , '5']);
|
||||
const graph = new Graph([[1, 2], [1, 3], [2, 5], [3, 5]]);
|
||||
expect(graph.expandOutputs([1])).toStrictEqual([2, 3 , 5]);
|
||||
});
|
||||
|
||||
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(['7'])).toStrictEqual([]);
|
||||
expect(graph.expandInputs(['6'])).toStrictEqual(['5', '2', '1']);
|
||||
expect(graph.expandInputs([7])).toStrictEqual([]);
|
||||
expect(graph.expandInputs([6])).toStrictEqual([5, 2, 1]);
|
||||
});
|
||||
|
||||
});
|
|
@ -1,10 +1,10 @@
|
|||
// ======== ID based fast Graph implementation =============
|
||||
export class GraphNode {
|
||||
id: string;
|
||||
outputs: string[];
|
||||
inputs: string[];
|
||||
id: number;
|
||||
outputs: number[];
|
||||
inputs: number[];
|
||||
|
||||
constructor(id: string) {
|
||||
constructor(id: number) {
|
||||
this.id = id;
|
||||
this.outputs = [];
|
||||
this.inputs = [];
|
||||
|
@ -17,29 +17,29 @@ export class GraphNode {
|
|||
return result;
|
||||
}
|
||||
|
||||
addOutput(node: string): void {
|
||||
addOutput(node: number): void {
|
||||
this.outputs.push(node);
|
||||
}
|
||||
|
||||
addInput(node: string): void {
|
||||
addInput(node: number): void {
|
||||
this.inputs.push(node);
|
||||
}
|
||||
|
||||
removeInput(target: string): string | null {
|
||||
removeInput(target: number): number | null {
|
||||
const index = this.inputs.findIndex(node => node === target);
|
||||
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);
|
||||
return index > -1 ? this.outputs.splice(index, 1)[0] : null;
|
||||
}
|
||||
}
|
||||
|
||||
export class Graph {
|
||||
nodes: Map<string, GraphNode> = new Map();
|
||||
nodes: Map<number, GraphNode> = new Map();
|
||||
|
||||
constructor(arr?: string[][]) {
|
||||
constructor(arr?: number[][]) {
|
||||
if (!arr) {
|
||||
return;
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
addNode(target: string): GraphNode {
|
||||
addNode(target: number): GraphNode {
|
||||
let node = this.nodes.get(target);
|
||||
if (!node) {
|
||||
node = new GraphNode(target);
|
||||
|
@ -67,11 +67,11 @@ export class Graph {
|
|||
return node;
|
||||
}
|
||||
|
||||
hasNode(target: string): boolean {
|
||||
hasNode(target: number): boolean {
|
||||
return !!this.nodes.get(target);
|
||||
}
|
||||
|
||||
removeNode(target: string): GraphNode | null {
|
||||
removeNode(target: number): GraphNode | null {
|
||||
const nodeToRemove = this.nodes.get(target);
|
||||
if (!nodeToRemove) {
|
||||
return null;
|
||||
|
@ -84,7 +84,7 @@ export class Graph {
|
|||
return nodeToRemove;
|
||||
}
|
||||
|
||||
foldNode(target: string): GraphNode | null {
|
||||
foldNode(target: number): GraphNode | null {
|
||||
const nodeToRemove = this.nodes.get(target);
|
||||
if (!nodeToRemove) {
|
||||
return null;
|
||||
|
@ -107,14 +107,14 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
addEdge(source: string, destination: string): void {
|
||||
addEdge(source: number, destination: number): void {
|
||||
const sourceNode = this.addNode(source);
|
||||
const destinationNode = this.addNode(destination);
|
||||
sourceNode.addOutput(destinationNode.id);
|
||||
destinationNode.addInput(sourceNode.id);
|
||||
}
|
||||
|
||||
removeEdge(source: string, destination: string): void {
|
||||
removeEdge(source: number, destination: number): void {
|
||||
const sourceNode = this.nodes.get(source);
|
||||
const destinationNode = this.nodes.get(destination);
|
||||
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);
|
||||
if (!sourceNode) {
|
||||
return false;
|
||||
|
@ -131,9 +131,9 @@ export class Graph {
|
|||
return !!sourceNode.outputs.find(id => id === destination);
|
||||
}
|
||||
|
||||
expandOutputs(origin: string[]): string[] {
|
||||
const result: string[] = [];
|
||||
const marked = new Map<string, boolean>();
|
||||
expandOutputs(origin: number[]): number[] {
|
||||
const result: number[] = [];
|
||||
const marked = new Map<number, boolean>();
|
||||
origin.forEach(id => marked.set(id, true));
|
||||
origin.forEach(id => {
|
||||
const node = this.nodes.get(id);
|
||||
|
@ -161,9 +161,9 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
expandInputs(origin: string[]): string[] {
|
||||
const result: string[] = [];
|
||||
const marked = new Map<string, boolean>();
|
||||
expandInputs(origin: number[]): number[] {
|
||||
const result: number[] = [];
|
||||
const marked = new Map<number, boolean>();
|
||||
origin.forEach(id => marked.set(id, true));
|
||||
origin.forEach(id => {
|
||||
const node = this.nodes.get(id);
|
||||
|
@ -191,10 +191,10 @@ export class Graph {
|
|||
return result;
|
||||
}
|
||||
|
||||
tolopogicalOrder(): string[] {
|
||||
const result: string[] = [];
|
||||
const marked = new Map<string, boolean>();
|
||||
const toVisit: string[] = [];
|
||||
tolopogicalOrder(): number[] {
|
||||
const result: number[] = [];
|
||||
const marked = new Map<number, boolean>();
|
||||
const toVisit: number[] = [];
|
||||
this.nodes.forEach(node => {
|
||||
if (marked.get(node.id)) {
|
||||
return;
|
||||
|
@ -225,12 +225,12 @@ export class Graph {
|
|||
|
||||
transitiveReduction() {
|
||||
const order = this.tolopogicalOrder();
|
||||
const marked = new Map<string, boolean>();
|
||||
const marked = new Map<number, boolean>();
|
||||
order.forEach(nodeID => {
|
||||
if (marked.get(nodeID)) {
|
||||
return;
|
||||
}
|
||||
const stack: {id: string, parents: string[]}[] = [];
|
||||
const stack: {id: number, parents: number[]}[] = [];
|
||||
stack.push({id: nodeID, parents: []});
|
||||
while (stack.length > 0) {
|
||||
const item = stack.splice(0, 1)[0];
|
||||
|
|
|
@ -196,7 +196,7 @@ export function postNewConstituenta(schema: string, request: FrontExchange<ICstC
|
|||
|
||||
export function patchDeleteConstituenta(schema: string, request: FrontExchange<IConstituentaList, IRSFormData>) {
|
||||
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/`,
|
||||
request: request
|
||||
});
|
||||
|
|
|
@ -152,6 +152,11 @@ export enum RSErrorType {
|
|||
typesNotCompatible = 0x8825, // Типы не совместимы в данном контексте
|
||||
orderingNotSupported = 0x8826, // Для данного типа не поддерживается порядок элементов
|
||||
|
||||
// !!!! Добавлены по сравнению с ConceptCore !!!!!
|
||||
globalNonemptyBase = 0x8827, // Непустое выражение базисного/константного множества
|
||||
globalUnexpectedType = 0x8828, // Типизация выражения не соответствует типу конституенты
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
globalNoValue = 0x8840, // Используется неинтерпретируемый глобальный идентификатор
|
||||
invalidPropertyUsage = 0x8841, // Использование свойства в качестве значения
|
||||
globalMissingAST = 0x8842, // Не удалось получить дерево разбора для глобального идентификатора
|
||||
|
|
|
@ -109,7 +109,7 @@ export enum CstClass {
|
|||
}
|
||||
|
||||
export interface IConstituenta {
|
||||
id: string
|
||||
id: number
|
||||
alias: string
|
||||
cstType: CstType
|
||||
convention: string
|
||||
|
@ -138,8 +138,8 @@ export interface IConstituenta {
|
|||
}
|
||||
|
||||
export interface IConstituentaMeta {
|
||||
id: string
|
||||
schema: string
|
||||
id: number
|
||||
schema: number
|
||||
order: number
|
||||
alias: string
|
||||
convention: string
|
||||
|
@ -158,7 +158,7 @@ export interface IConstituentaList {
|
|||
|
||||
export interface ICstCreateData
|
||||
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 {
|
||||
|
@ -196,7 +196,7 @@ export interface IRSFormStats {
|
|||
}
|
||||
|
||||
export interface IRSForm {
|
||||
id: string
|
||||
id: number
|
||||
title: string
|
||||
alias: string
|
||||
comment: string
|
||||
|
@ -368,9 +368,7 @@ export function LoadRSFormData(schema: IRSFormData): IRSForm {
|
|||
count_theorem: result.items.reduce(
|
||||
(sum, cst) => sum + (cst.cstType === CstType.THEOREM ? 1 : 0), 0)
|
||||
}
|
||||
result.id = String(result.id)
|
||||
result.items.forEach(cst => {
|
||||
cst.id = String(cst.id)
|
||||
cst.status = inferStatus(cst.parse.status, cst.parse.valueClass);
|
||||
cst.isTemplate = inferTemplate(cst.definition.formal);
|
||||
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) {
|
||||
return schema.items;
|
||||
}
|
||||
let ids: string[] | undefined = undefined
|
||||
let ids: number[] | undefined = undefined
|
||||
switch (mode) {
|
||||
case DependencyMode.OUTPUTS: { ids = schema.graph.nodes.get(start)?.outputs; break; }
|
||||
case DependencyMode.INPUTS: { ids = schema.graph.nodes.get(start)?.inputs; break; }
|
||||
|
|
|
@ -407,7 +407,7 @@ export function createAliasFor(type: CstType, schema: IRSForm): string {
|
|||
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 {
|
||||
id: id,
|
||||
alias: alias,
|
||||
|
@ -568,6 +568,10 @@ export function getRSErrorMessage(error: IRSErrorDescription): string {
|
|||
return `Не удалось получить дерево разбора для глобального идентификатора: ${error.params[0]}`;
|
||||
case RSErrorType.globalFuncNoInterpretation:
|
||||
return `Функция не интерпретируется для данных аргументов`;
|
||||
case RSErrorType.globalNonemptyBase:
|
||||
return `Непустое выражение базисного/константного множества`;
|
||||
case RSErrorType.globalUnexpectedType:
|
||||
return `Типизация выражения не соответствует типу конституенты`;
|
||||
}
|
||||
return 'UNKNOWN ERROR';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user