F: Prepare frontend for Synthesis execution

This commit is contained in:
Ivan 2024-07-29 23:14:45 +03:00
parent 0a7cfa1375
commit afd3f5f7e4
10 changed files with 116 additions and 30 deletions

View File

@ -1,5 +1,4 @@
''' Serializers for persistent data manipulation. ''' ''' Serializers for persistent data manipulation. '''
import re
from typing import cast from typing import cast
from django.db.models import F from django.db.models import F

View File

@ -73,3 +73,10 @@ export function postExecuteOperation(oss: string, request: FrontExchange<ITarget
request: request request: request
}); });
} }
export function postExecuteAll(oss: string, request: FrontExchange<IPositionsData, IOperationSchemaData>) {
AxiosPost({
endpoint: `/api/oss/${oss}/execute-all`,
request: request
});
}

View File

@ -108,7 +108,7 @@ export { LuNetwork as IconGenerateStructure } from 'react-icons/lu';
export { LuBookCopy as IconInlineSynthesis } from 'react-icons/lu'; export { LuBookCopy as IconInlineSynthesis } from 'react-icons/lu';
export { LuWand2 as IconGenerateNames } from 'react-icons/lu'; export { LuWand2 as IconGenerateNames } from 'react-icons/lu';
export { GrConnect as IconConnect } from 'react-icons/gr'; export { GrConnect as IconConnect } from 'react-icons/gr';
export { BsPlay as IconExecute } from 'react-icons/bs'; export { BiPlayCircle as IconExecute } from 'react-icons/bi';
// ======== Graph UI ======= // ======== Graph UI =======
export { BiCollapse as IconGraphCollapse } from 'react-icons/bi'; export { BiCollapse as IconGraphCollapse } from 'react-icons/bi';

View File

@ -223,6 +223,7 @@ function PickSubstitutions({
<div className='flex flex-col gap-[0.125rem] border-x border-t clr-input'> <div className='flex flex-col gap-[0.125rem] border-x border-t clr-input'>
<SelectOperation <SelectOperation
noBorder noBorder
placeholder='Выберите аргумент'
items={operations.filter(item => item.id !== rightArgument?.id)} items={operations.filter(item => item.id !== rightArgument?.id)}
value={leftArgument} value={leftArgument}
onSelectValue={setLeftArgument} onSelectValue={setLeftArgument}
@ -275,6 +276,7 @@ function PickSubstitutions({
<div className='flex flex-col gap-[0.125rem] border-x border-t clr-input'> <div className='flex flex-col gap-[0.125rem] border-x border-t clr-input'>
<SelectOperation <SelectOperation
noBorder noBorder
placeholder='Выберите аргумент'
items={operations.filter(item => item.id !== leftArgument?.id)} items={operations.filter(item => item.id !== leftArgument?.id)}
value={rightArgument} value={rightArgument}
onSelectValue={setRightArgument} onSelectValue={setRightArgument}

View File

@ -19,6 +19,7 @@ import {
patchUpdateOperation, patchUpdateOperation,
patchUpdatePositions, patchUpdatePositions,
postCreateOperation, postCreateOperation,
postExecuteAll,
postExecuteOperation postExecuteOperation
} from '@/backend/oss'; } from '@/backend/oss';
import { type ErrorData } from '@/components/info/InfoError'; import { type ErrorData } from '@/components/info/InfoError';
@ -68,6 +69,7 @@ interface IOssContext {
setInput: (data: IOperationSetInputData, callback?: () => void) => void; setInput: (data: IOperationSetInputData, callback?: () => void) => void;
updateOperation: (data: IOperationUpdateData, callback?: () => void) => void; updateOperation: (data: IOperationUpdateData, callback?: () => void) => void;
executeOperation: (data: ITargetOperation, callback?: () => void) => void; executeOperation: (data: ITargetOperation, callback?: () => void) => void;
executeAll: (data: IPositionsData, callback?: () => void) => void;
} }
const OssContext = createContext<IOssContext | null>(null); const OssContext = createContext<IOssContext | null>(null);
@ -411,6 +413,28 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
[itemID, schema, library] [itemID, schema, library]
); );
const executeAll = useCallback(
(data: IPositionsData, callback?: () => void) => {
if (!schema) {
return;
}
setProcessingError(undefined);
postExecuteAll(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
library.setGlobalOSS(newData);
library.reloadItems(() => {
if (callback) callback();
});
}
});
},
[itemID, schema, library]
);
return ( return (
<OssContext.Provider <OssContext.Provider
value={{ value={{
@ -437,7 +461,8 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
createInput, createInput,
setInput, setInput,
updateOperation, updateOperation,
executeOperation executeOperation,
executeAll
}} }}
> >
{children} {children}

View File

@ -24,7 +24,7 @@ interface NodeContextMenuProps extends ContextMenuData {
onCreateInput: (target: OperationID) => void; onCreateInput: (target: OperationID) => void;
onEditSchema: (target: OperationID) => void; onEditSchema: (target: OperationID) => void;
onEditOperation: (target: OperationID) => void; onEditOperation: (target: OperationID) => void;
onRunOperation: (target: OperationID) => void; onExecuteOperation: (target: OperationID) => void;
} }
function NodeContextMenu({ function NodeContextMenu({
@ -36,7 +36,7 @@ function NodeContextMenu({
onCreateInput, onCreateInput,
onEditSchema, onEditSchema,
onEditOperation, onEditOperation,
onRunOperation onExecuteOperation
}: NodeContextMenuProps) { }: NodeContextMenuProps) {
const controller = useOssEdit(); const controller = useOssEdit();
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
@ -97,7 +97,7 @@ function NodeContextMenu({
const handleRunSynthesis = () => { const handleRunSynthesis = () => {
handleHide(); handleHide();
onRunOperation(operation.id); onExecuteOperation(operation.id);
}; };
return ( return (

View File

@ -162,13 +162,21 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
[controller, getPositions] [controller, getPositions]
); );
const handleRunOperation = useCallback( const handleExecuteOperation = useCallback(
(target: OperationID) => { (target: OperationID) => {
controller.runOperation(target, getPositions()); controller.executeOperation(target, getPositions());
}, },
[controller, getPositions] [controller, getPositions]
); );
const handleExecuteSelected = useCallback(() => {
if (controller.selected.length === 1) {
handleExecuteOperation(controller.selected[0]);
} else {
controller.executeAll(getPositions());
}
}, [controller, handleExecuteOperation, getPositions]);
const handleFitView = useCallback(() => { const handleFitView = useCallback(() => {
flow.fitView({ duration: PARAMETER.zoomDuration }); flow.fitView({ duration: PARAMETER.zoomDuration });
}, [flow]); }, [flow]);
@ -285,8 +293,8 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
onNodesChange={handleNodesChange} onNodesChange={handleNodesChange}
onEdgesChange={onEdgesChange} onEdgesChange={onEdgesChange}
onNodeClick={handleNodeClick} onNodeClick={handleNodeClick}
fitView
proOptions={{ hideAttribution: true }} proOptions={{ hideAttribution: true }}
fitView
nodeTypes={OssNodeTypes} nodeTypes={OssNodeTypes}
maxZoom={2} maxZoom={2}
minZoom={0.75} minZoom={0.75}
@ -299,7 +307,17 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
{showGrid ? <Background gap={10} /> : null} {showGrid ? <Background gap={10} /> : null}
</ReactFlow> </ReactFlow>
), ),
[nodes, edges, handleNodesChange, handleContextMenu, handleClickCanvas, onEdgesChange, OssNodeTypes, showGrid] [
nodes,
edges,
handleNodesChange,
handleContextMenu,
handleClickCanvas,
onEdgesChange,
handleNodeClick,
OssNodeTypes,
showGrid
]
); );
return ( return (
@ -313,6 +331,8 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
onFitView={handleFitView} onFitView={handleFitView}
onCreate={handleCreateOperation} onCreate={handleCreateOperation}
onDelete={handleDeleteSelected} onDelete={handleDeleteSelected}
onEdit={() => handleEditOperation(controller.selected[0])}
onExecute={handleExecuteSelected}
onResetPositions={handleResetPositions} onResetPositions={handleResetPositions}
onSavePositions={handleSavePositions} onSavePositions={handleSavePositions}
onSaveImage={handleSaveImage} onSaveImage={handleSaveImage}
@ -328,7 +348,7 @@ function OssFlow({ isModified, setIsModified }: OssFlowProps) {
onCreateInput={handleCreateInput} onCreateInput={handleCreateInput}
onEditSchema={handleEditSchema} onEditSchema={handleEditSchema}
onEditOperation={handleEditOperation} onEditOperation={handleEditOperation}
onRunOperation={handleRunOperation} onExecuteOperation={handleExecuteOperation}
{...menuProps} {...menuProps}
/> />
) : null} ) : null}

View File

@ -4,6 +4,8 @@ import {
IconAnimation, IconAnimation,
IconAnimationOff, IconAnimationOff,
IconDestroy, IconDestroy,
IconEdit2,
IconExecute,
IconFitImage, IconFitImage,
IconGrid, IconGrid,
IconImage, IconImage,
@ -28,6 +30,8 @@ interface ToolbarOssGraphProps {
edgeStraight: boolean; edgeStraight: boolean;
onCreate: () => void; onCreate: () => void;
onDelete: () => void; onDelete: () => void;
onEdit: () => void;
onExecute: () => void;
onFitView: () => void; onFitView: () => void;
onSaveImage: () => void; onSaveImage: () => void;
onSavePositions: () => void; onSavePositions: () => void;
@ -44,6 +48,8 @@ function ToolbarOssGraph({
edgeStraight, edgeStraight,
onCreate, onCreate,
onDelete, onDelete,
onEdit,
onExecute,
onFitView, onFitView,
onSaveImage, onSaveImage,
onSavePositions, onSavePositions,
@ -57,6 +63,12 @@ function ToolbarOssGraph({
return ( return (
<div className='flex flex-col items-center'> <div className='flex flex-col items-center'>
<div className='cc-icons'> <div className='cc-icons'>
<MiniButton
title='Сбросить изменения'
icon={<IconReset size='1.25rem' className='icon-primary' />}
disabled={!isModified}
onClick={onResetPositions}
/>
<MiniButton <MiniButton
icon={<IconFitImage size='1.25rem' className='icon-primary' />} icon={<IconFitImage size='1.25rem' className='icon-primary' />}
title='Сбросить вид' title='Сбросить вид'
@ -116,19 +128,30 @@ function ToolbarOssGraph({
onClick={onSavePositions} onClick={onSavePositions}
/> />
<MiniButton <MiniButton
title='Сбросить изменения' title={prepareTooltip('Новая операция', 'Ctrl + Q')}
icon={<IconReset size='1.25rem' className='icon-primary' />}
disabled={!isModified}
onClick={onResetPositions}
/>
<MiniButton
title='Новая операция'
icon={<IconNewItem size='1.25rem' className='icon-green' />} icon={<IconNewItem size='1.25rem' className='icon-green' />}
disabled={controller.isProcessing} disabled={controller.isProcessing}
onClick={onCreate} onClick={onCreate}
/> />
<MiniButton <MiniButton
title='Удалить выбранную' title='Выполнить выбранную / все операции'
icon={
<IconExecute
size='1.25rem'
className={controller.selected.length === 1 ? 'icon-primary' : 'icon-green'}
/>
}
disabled={controller.isProcessing}
onClick={onExecute}
/>
<MiniButton
titleHtml={prepareTooltip('Редактировать выбранную', 'Ctrl + клик')}
icon={<IconEdit2 size='1.25rem' className='icon-primary' />}
disabled={controller.selected.length !== 1 || controller.isProcessing}
onClick={onEdit}
/>
<MiniButton
titleHtml={prepareTooltip('Удалить выбранную', 'Delete')}
icon={<IconDestroy size='1.25rem' className='icon-red' />} icon={<IconDestroy size='1.25rem' className='icon-red' />}
disabled={controller.selected.length !== 1 || controller.isProcessing} disabled={controller.selected.length !== 1 || controller.isProcessing}
onClick={onDelete} onClick={onDelete}
@ -138,5 +161,5 @@ function ToolbarOssGraph({
</div> </div>
); );
} }
//IconExecute
export default ToolbarOssGraph; export default ToolbarOssGraph;

View File

@ -56,7 +56,8 @@ export interface IOssEditContext {
createInput: (target: OperationID, positions: IOperationPosition[]) => void; createInput: (target: OperationID, positions: IOperationPosition[]) => void;
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void; promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
promptEditOperation: (target: OperationID, positions: IOperationPosition[]) => void; promptEditOperation: (target: OperationID, positions: IOperationPosition[]) => void;
runOperation: (target: OperationID, positions: IOperationPosition[]) => void; executeOperation: (target: OperationID, positions: IOperationPosition[]) => void;
executeAll: (positions: IOperationPosition[]) => void;
} }
const OssEditContext = createContext<IOssEditContext | null>(null); const OssEditContext = createContext<IOssEditContext | null>(null);
@ -279,15 +280,21 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
[model, targetOperationID, positions] [model, targetOperationID, positions]
); );
const runOperation = useCallback( const executeOperation = useCallback(
(target: OperationID, positions: IOperationPosition[]) => { (target: OperationID, positions: IOperationPosition[]) => {
model.executeOperation( const data = {
{
target: target, target: target,
positions: positions positions: positions
};
model.executeOperation(data, () => toast.success(information.operationExecuted));
}, },
() => toast.success(information.changesSaved) [model]
); );
const executeAll = useCallback(
(positions: IOperationPosition[]) => {
const data = { positions: positions };
model.executeAll(data, () => toast.success(information.allOperationExecuted));
}, },
[model] [model]
); );
@ -320,7 +327,8 @@ export const OssEditState = ({ selected, setSelected, children }: OssEditStatePr
createInput, createInput,
promptEditInput, promptEditInput,
promptEditOperation, promptEditOperation,
runOperation executeOperation,
executeAll
}} }}
> >
{model.schema ? ( {model.schema ? (

View File

@ -940,6 +940,8 @@ export const information = {
versionDestroyed: 'Версия удалена', versionDestroyed: 'Версия удалена',
itemDestroyed: 'Схема удалена', itemDestroyed: 'Схема удалена',
operationDestroyed: 'Операция удалена', operationDestroyed: 'Операция удалена',
operationExecuted: 'Операция выполнена',
allOperationExecuted: 'Все операции выполнены',
constituentsDestroyed: (aliases: string) => `Конституенты удалены: ${aliases}` constituentsDestroyed: (aliases: string) => `Конституенты удалены: ${aliases}`
}; };