Implementing basic oss graph pt1
This commit is contained in:
parent
f91f42ff5b
commit
286abaf476
|
@ -36,6 +36,7 @@ This readme file is used mostly to document project dependencies
|
|||
- react-error-boundary
|
||||
- react-pdf
|
||||
- react-tooltip
|
||||
- reactflow
|
||||
- js-file-download
|
||||
- use-debounce
|
||||
- framer-motion
|
||||
|
@ -54,6 +55,7 @@ This readme file is used mostly to document project dependencies
|
|||
- autoprefixer
|
||||
- eslint-plugin-simple-import-sort
|
||||
- eslint-plugin-tsdoc
|
||||
- vite
|
||||
- jest
|
||||
- ts-jest
|
||||
- @types/jest
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
from apps.rsform.serializers import LibraryItemSerializer
|
||||
|
||||
from .basics import OperationPositionSerializer, PositionsSerializer
|
||||
from .basics import OperationPositionSerializer, PositionsSerializer, SubstitutionExSerializer
|
||||
from .data_access import (
|
||||
ArgumentSerializer,
|
||||
OperationCreateSerializer,
|
||||
|
|
|
@ -14,3 +14,15 @@ class PositionsSerializer(serializers.Serializer):
|
|||
positions = serializers.ListField(
|
||||
child=OperationPositionSerializer()
|
||||
)
|
||||
|
||||
|
||||
class SubstitutionExSerializer(serializers.Serializer):
|
||||
''' Serializer: Substitution extended data. '''
|
||||
operation = serializers.IntegerField()
|
||||
original = serializers.IntegerField()
|
||||
substitution = serializers.IntegerField()
|
||||
transfer_term = serializers.BooleanField()
|
||||
original_alias = serializers.CharField()
|
||||
original_term = serializers.CharField()
|
||||
substitution_alias = serializers.CharField()
|
||||
substitution_term = serializers.CharField()
|
||||
|
|
|
@ -10,7 +10,7 @@ from apps.rsform.serializers import LibraryItemDetailsSerializer
|
|||
from shared import messages as msg
|
||||
|
||||
from ..models import Argument, Operation, OperationSchema, OperationType
|
||||
from .basics import OperationPositionSerializer
|
||||
from .basics import OperationPositionSerializer, SubstitutionExSerializer
|
||||
|
||||
|
||||
class OperationSerializer(serializers.ModelSerializer):
|
||||
|
@ -75,9 +75,12 @@ class OperationSchemaSerializer(serializers.ModelSerializer):
|
|||
items = serializers.ListField(
|
||||
child=OperationSerializer()
|
||||
)
|
||||
graph = serializers.ListField(
|
||||
arguments = serializers.ListField(
|
||||
child=ArgumentSerializer()
|
||||
)
|
||||
substitutions = serializers.ListField(
|
||||
child=SubstitutionExSerializer()
|
||||
)
|
||||
|
||||
class Meta:
|
||||
''' serializer metadata. '''
|
||||
|
@ -90,15 +93,15 @@ class OperationSchemaSerializer(serializers.ModelSerializer):
|
|||
result['items'] = []
|
||||
for operation in oss.operations():
|
||||
result['items'].append(OperationSerializer(operation).data)
|
||||
result['graph'] = []
|
||||
result['arguments'] = []
|
||||
for argument in oss.arguments():
|
||||
result['graph'].append(ArgumentSerializer(argument).data)
|
||||
result['arguments'].append(ArgumentSerializer(argument).data)
|
||||
result['substitutions'] = []
|
||||
for substitution in oss.substitutions().values(
|
||||
'operation',
|
||||
'original',
|
||||
'transfer_term',
|
||||
'substitution',
|
||||
'transfer_term',
|
||||
original_alias=F('original__alias'),
|
||||
original_term=F('original__term_resolved'),
|
||||
substitution_alias=F('substitution__alias'),
|
||||
|
|
|
@ -77,12 +77,12 @@ class TestOssViewset(EndpointTester):
|
|||
self.assertEqual(sub['substitution_alias'], self.ks2x1.alias)
|
||||
self.assertEqual(sub['substitution_term'], self.ks2x1.term_resolved)
|
||||
|
||||
graph = response.data['graph']
|
||||
self.assertEqual(len(graph), 2)
|
||||
self.assertEqual(graph[0]['operation'], self.operation3.pk)
|
||||
self.assertEqual(graph[0]['argument'], self.operation1.pk)
|
||||
self.assertEqual(graph[1]['operation'], self.operation3.pk)
|
||||
self.assertEqual(graph[1]['argument'], self.operation2.pk)
|
||||
arguments = response.data['arguments']
|
||||
self.assertEqual(len(arguments), 2)
|
||||
self.assertEqual(arguments[0]['operation'], self.operation3.pk)
|
||||
self.assertEqual(arguments[0]['argument'], self.operation1.pk)
|
||||
self.assertEqual(arguments[1]['operation'], self.operation3.pk)
|
||||
self.assertEqual(arguments[1]['argument'], self.operation2.pk)
|
||||
|
||||
self.executeOK(item=self.unowned_id)
|
||||
self.executeForbidden(item=self.private_id)
|
||||
|
|
|
@ -7,17 +7,14 @@ import { toast } from 'react-toastify';
|
|||
|
||||
import { type ErrorData } from '@/components/info/InfoError';
|
||||
import { ILexemeData, ITextRequest, ITextResult, IWordFormPlain } from '@/models/language';
|
||||
import {
|
||||
AccessPolicy,
|
||||
ILibraryItem,
|
||||
ILibraryUpdateData,
|
||||
ITargetAccessPolicy,
|
||||
ITargetLocation,
|
||||
IVersionData,
|
||||
LibraryItemType
|
||||
} from '@/models/library';
|
||||
import { ILibraryItem, ILibraryUpdateData, ITargetAccessPolicy, ITargetLocation, IVersionData } from '@/models/library';
|
||||
import { ILibraryCreateData } from '@/models/library';
|
||||
import { IOperationSchemaData } from '@/models/oss';
|
||||
import {
|
||||
ICstSubstituteData,
|
||||
IOperationCreateData,
|
||||
IOperationCreatedResponse,
|
||||
IOperationSchemaData
|
||||
} from '@/models/oss';
|
||||
import {
|
||||
IConstituentaList,
|
||||
IConstituentaMeta,
|
||||
|
@ -25,7 +22,6 @@ import {
|
|||
ICstCreatedResponse,
|
||||
ICstMovetoData,
|
||||
ICstRenameData,
|
||||
ICstSubstituteData,
|
||||
ICstUpdateData,
|
||||
IInlineSynthesisData,
|
||||
IProduceStructureResponse,
|
||||
|
@ -233,30 +229,6 @@ export function postCloneLibraryItem(target: string, request: FrontExchange<IRSF
|
|||
});
|
||||
}
|
||||
|
||||
export function getOssDetails(target: string, request: FrontPull<IOperationSchemaData>) {
|
||||
request.setLoading!(false);
|
||||
request.onSuccess({
|
||||
id: Number(target),
|
||||
comment: '123',
|
||||
alias: 'oss1',
|
||||
access_policy: AccessPolicy.PUBLIC,
|
||||
editors: [],
|
||||
owner: 1,
|
||||
item_type: LibraryItemType.OSS,
|
||||
location: '/U',
|
||||
read_only: false,
|
||||
subscribers: [],
|
||||
time_create: '0',
|
||||
time_update: '0',
|
||||
title: 'TestOss',
|
||||
visible: false
|
||||
});
|
||||
// AxiosGet({
|
||||
// endpoint: `/api/oss/${target}`, // TODO: endpoint to access OSS
|
||||
// request: request
|
||||
// });
|
||||
}
|
||||
|
||||
export function getRSFormDetails(target: string, version: string, request: FrontPull<IRSFormData>) {
|
||||
if (!version) {
|
||||
AxiosGet({
|
||||
|
@ -357,7 +329,7 @@ export function getTRSFile(target: string, version: string, request: FrontPull<B
|
|||
}
|
||||
}
|
||||
|
||||
export function postNewConstituenta(schema: string, request: FrontExchange<ICstCreateData, ICstCreatedResponse>) {
|
||||
export function postCreateConstituenta(schema: string, request: FrontExchange<ICstCreateData, ICstCreatedResponse>) {
|
||||
AxiosPost({
|
||||
endpoint: `/api/rsforms/${schema}/cst-create`,
|
||||
request: request
|
||||
|
@ -445,6 +417,23 @@ export function patchInlineSynthesis(request: FrontExchange<IInlineSynthesisData
|
|||
});
|
||||
}
|
||||
|
||||
export function getOssDetails(target: string, request: FrontPull<IOperationSchemaData>) {
|
||||
AxiosGet({
|
||||
endpoint: `/api/oss/${target}/details`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export function postCreateOperation(
|
||||
schema: string,
|
||||
request: FrontExchange<IOperationCreateData, IOperationCreatedResponse>
|
||||
) {
|
||||
AxiosPost({
|
||||
endpoint: `/api/oss/${schema}/create-operation`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export function postInflectText(request: FrontExchange<IWordFormPlain, ITextResult>) {
|
||||
AxiosPost({
|
||||
endpoint: `/api/cctext/inflect`,
|
||||
|
|
|
@ -8,7 +8,7 @@ import DataTable, { createColumnHelper } from '@/components/ui/DataTable';
|
|||
import Label from '@/components/ui/Label';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { IConstituenta, IRSForm, ISubstitution } from '@/models/rsform';
|
||||
import { IConstituenta, IRSForm, ISingleSubstitution } from '@/models/rsform';
|
||||
import { describeConstituenta } from '@/utils/labels';
|
||||
|
||||
import {
|
||||
|
@ -34,11 +34,11 @@ interface PickSubstitutionsProps {
|
|||
filter1?: (cst: IConstituenta) => boolean;
|
||||
filter2?: (cst: IConstituenta) => boolean;
|
||||
|
||||
items: ISubstitution[];
|
||||
setItems: React.Dispatch<React.SetStateAction<ISubstitution[]>>;
|
||||
items: ISingleSubstitution[];
|
||||
setItems: React.Dispatch<React.SetStateAction<ISingleSubstitution[]>>;
|
||||
}
|
||||
|
||||
function SubstitutionIcon({ item }: { item: ISubstitution }) {
|
||||
function SubstitutionIcon({ item }: { item: ISingleSubstitution }) {
|
||||
if (item.deleteRight) {
|
||||
if (item.takeLeftTerm) {
|
||||
return <IconPageRight size='1.2rem' />;
|
||||
|
@ -54,7 +54,7 @@ function SubstitutionIcon({ item }: { item: ISubstitution }) {
|
|||
}
|
||||
}
|
||||
|
||||
const columnHelper = createColumnHelper<ISubstitution>();
|
||||
const columnHelper = createColumnHelper<ISingleSubstitution>();
|
||||
|
||||
function PickSubstitutions({
|
||||
items,
|
||||
|
@ -80,7 +80,7 @@ function PickSubstitutions({
|
|||
if (!leftCst || !rightCst) {
|
||||
return;
|
||||
}
|
||||
const newSubstitution: ISubstitution = {
|
||||
const newSubstitution: ISingleSubstitution = {
|
||||
leftCst: leftCst,
|
||||
rightCst: rightCst,
|
||||
deleteRight: deleteRight,
|
||||
|
@ -99,7 +99,7 @@ function PickSubstitutions({
|
|||
const handleDeleteRow = useCallback(
|
||||
(row: number) => {
|
||||
setItems(prev => {
|
||||
const newItems: ISubstitution[] = [];
|
||||
const newItems: ISingleSubstitution[] = [];
|
||||
prev.forEach((item, index) => {
|
||||
if (index !== row) {
|
||||
newItems.push(item);
|
||||
|
|
|
@ -10,13 +10,14 @@ import {
|
|||
patchSetAccessPolicy,
|
||||
patchSetLocation,
|
||||
patchSetOwner,
|
||||
postCreateOperation,
|
||||
postSubscribe
|
||||
} from '@/app/backendAPI';
|
||||
import { type ErrorData } from '@/components/info/InfoError';
|
||||
import useOssDetails from '@/hooks/useOssDetails';
|
||||
import { AccessPolicy, ILibraryItem } from '@/models/library';
|
||||
import { ILibraryUpdateData } from '@/models/library';
|
||||
import { IOperationSchema } from '@/models/oss';
|
||||
import { IOperation, IOperationCreateData, IOperationSchema } from '@/models/oss';
|
||||
import { UserID } from '@/models/user';
|
||||
import { contextOutsideScope } from '@/utils/labels';
|
||||
|
||||
|
@ -43,6 +44,8 @@ interface IOssContext {
|
|||
setAccessPolicy: (newPolicy: AccessPolicy, callback?: () => void) => void;
|
||||
setLocation: (newLocation: string, callback?: () => void) => void;
|
||||
setEditors: (newEditors: UserID[], callback?: () => void) => void;
|
||||
|
||||
createOperation: (data: IOperationCreateData, callback?: DataCallback<IOperation>) => void;
|
||||
}
|
||||
|
||||
const OssContext = createContext<IOssContext | null>(null);
|
||||
|
@ -63,13 +66,11 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
|
|||
const library = useLibrary();
|
||||
const { user } = useAuth();
|
||||
const {
|
||||
schema: schema, // prettier: split lines
|
||||
schema, // prettier: split lines
|
||||
error: errorLoading,
|
||||
setSchema,
|
||||
loading
|
||||
} = useOssDetails({
|
||||
target: itemID
|
||||
});
|
||||
} = useOssDetails({ target: itemID });
|
||||
const [processing, setProcessing] = useState(false);
|
||||
const [processingError, setProcessingError] = useState<ErrorData>(undefined);
|
||||
|
||||
|
@ -249,6 +250,24 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
|
|||
[itemID, schema]
|
||||
);
|
||||
|
||||
const createOperation = useCallback(
|
||||
(data: IOperationCreateData, callback?: DataCallback<IOperation>) => {
|
||||
setProcessingError(undefined);
|
||||
postCreateOperation(itemID, {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: newData => {
|
||||
setSchema(newData.oss);
|
||||
library.localUpdateTimestamp(newData.oss.id);
|
||||
if (callback) callback(newData.new_operation);
|
||||
}
|
||||
});
|
||||
},
|
||||
[itemID, library, setSchema]
|
||||
);
|
||||
|
||||
return (
|
||||
<OssContext.Provider
|
||||
value={{
|
||||
|
@ -267,7 +286,9 @@ export const OssState = ({ itemID, children }: OssStateProps) => {
|
|||
setOwner,
|
||||
setEditors,
|
||||
setAccessPolicy,
|
||||
setLocation
|
||||
setLocation,
|
||||
|
||||
createOperation
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -24,14 +24,15 @@ import {
|
|||
patchSubstituteConstituents,
|
||||
patchUploadTRS,
|
||||
patchVersion,
|
||||
postCreateConstituenta,
|
||||
postCreateVersion,
|
||||
postNewConstituenta,
|
||||
postSubscribe
|
||||
} from '@/app/backendAPI';
|
||||
import { type ErrorData } from '@/components/info/InfoError';
|
||||
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
||||
import { AccessPolicy, ILibraryItem, IVersionData, VersionID } from '@/models/library';
|
||||
import { ILibraryUpdateData } from '@/models/library';
|
||||
import { ICstSubstituteData } from '@/models/oss';
|
||||
import {
|
||||
ConstituentaID,
|
||||
IConstituentaList,
|
||||
|
@ -39,7 +40,6 @@ import {
|
|||
ICstCreateData,
|
||||
ICstMovetoData,
|
||||
ICstRenameData,
|
||||
ICstSubstituteData,
|
||||
ICstUpdateData,
|
||||
IInlineSynthesisData,
|
||||
IRSForm,
|
||||
|
@ -399,7 +399,7 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
const cstCreate = useCallback(
|
||||
(data: ICstCreateData, callback?: DataCallback<IConstituentaMeta>) => {
|
||||
setProcessingError(undefined);
|
||||
postNewConstituenta(itemID, {
|
||||
postCreateConstituenta(itemID, {
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
|
|
|
@ -8,7 +8,7 @@ import Modal, { ModalProps } from '@/components/ui/Modal';
|
|||
import TabLabel from '@/components/ui/TabLabel';
|
||||
import useRSFormDetails from '@/hooks/useRSFormDetails';
|
||||
import { LibraryItemID } from '@/models/library';
|
||||
import { IInlineSynthesisData, IRSForm, ISubstitution } from '@/models/rsform';
|
||||
import { IInlineSynthesisData, IRSForm, ISingleSubstitution } from '@/models/rsform';
|
||||
|
||||
import TabConstituents from './TabConstituents';
|
||||
import TabSchema from './TabSchema';
|
||||
|
@ -30,7 +30,7 @@ function DlgInlineSynthesis({ hideWindow, receiver, onInlineSynthesis }: DlgInli
|
|||
|
||||
const [donorID, setDonorID] = useState<LibraryItemID | undefined>(undefined);
|
||||
const [selected, setSelected] = useState<LibraryItemID[]>([]);
|
||||
const [substitutions, setSubstitutions] = useState<ISubstitution[]>([]);
|
||||
const [substitutions, setSubstitutions] = useState<ISingleSubstitution[]>([]);
|
||||
|
||||
const source = useRSFormDetails({ target: donorID ? String(donorID) : undefined });
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { ErrorData } from '@/components/info/InfoError';
|
||||
import DataLoader from '@/components/wrap/DataLoader';
|
||||
import { ConstituentaID, IRSForm, ISubstitution } from '@/models/rsform';
|
||||
import { ConstituentaID, IRSForm, ISingleSubstitution } from '@/models/rsform';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
import PickSubstitutions from '../../components/select/PickSubstitutions';
|
||||
|
@ -15,8 +15,8 @@ interface TabSubstitutionsProps {
|
|||
loading?: boolean;
|
||||
error?: ErrorData;
|
||||
|
||||
substitutions: ISubstitution[];
|
||||
setSubstitutions: React.Dispatch<React.SetStateAction<ISubstitution[]>>;
|
||||
substitutions: ISingleSubstitution[];
|
||||
setSubstitutions: React.Dispatch<React.SetStateAction<ISingleSubstitution[]>>;
|
||||
}
|
||||
|
||||
function TabSubstitutions({
|
||||
|
|
|
@ -6,7 +6,8 @@ import { useMemo, useState } from 'react';
|
|||
import PickSubstitutions from '@/components/select/PickSubstitutions';
|
||||
import Modal, { ModalProps } from '@/components/ui/Modal';
|
||||
import { useRSForm } from '@/context/RSFormContext';
|
||||
import { ICstSubstituteData, ISubstitution } from '@/models/rsform';
|
||||
import { ICstSubstituteData } from '@/models/oss';
|
||||
import { ISingleSubstitution } from '@/models/rsform';
|
||||
import { prefixes } from '@/utils/constants';
|
||||
|
||||
interface DlgSubstituteCstProps extends Pick<ModalProps, 'hideWindow'> {
|
||||
|
@ -16,7 +17,7 @@ interface DlgSubstituteCstProps extends Pick<ModalProps, 'hideWindow'> {
|
|||
function DlgSubstituteCst({ hideWindow, onSubstitute }: DlgSubstituteCstProps) {
|
||||
const { schema } = useRSForm();
|
||||
|
||||
const [substitutions, setSubstitutions] = useState<ISubstitution[]>([]);
|
||||
const [substitutions, setSubstitutions] = useState<ISingleSubstitution[]>([]);
|
||||
|
||||
const canSubmit = useMemo(() => substitutions.length > 0, [substitutions]);
|
||||
|
||||
|
|
|
@ -2,22 +2,40 @@
|
|||
* Module: OSS data loading and processing.
|
||||
*/
|
||||
|
||||
import { IOperationSchema, IOperationSchemaData } from './oss';
|
||||
import { Graph } from './Graph';
|
||||
import { IOperation, IOperationSchema, IOperationSchemaData, OperationID } from './oss';
|
||||
|
||||
/**
|
||||
* Loads data into an {@link IOperationSchema} based on {@link IOperationSchemaData}.
|
||||
*
|
||||
*/
|
||||
export class OssLoader {
|
||||
private schema: IOperationSchemaData;
|
||||
private oss: IOperationSchemaData;
|
||||
private graph: Graph = new Graph();
|
||||
private operationByID: Map<OperationID, IOperation> = new Map();
|
||||
|
||||
constructor(input: IOperationSchemaData) {
|
||||
this.schema = input;
|
||||
this.oss = input;
|
||||
}
|
||||
|
||||
produceOSS(): IOperationSchema {
|
||||
const result = this.schema as IOperationSchema;
|
||||
result.producedData = [1, 2, 3]; // TODO: put data processing here
|
||||
const result = this.oss as IOperationSchema;
|
||||
this.prepareLookups();
|
||||
this.createGraph();
|
||||
|
||||
result.operationByID = this.operationByID;
|
||||
result.graph = this.graph;
|
||||
return result;
|
||||
}
|
||||
|
||||
private prepareLookups() {
|
||||
this.oss.items.forEach(operation => {
|
||||
this.operationByID.set(operation.id, operation);
|
||||
this.graph.addNode(operation.id);
|
||||
});
|
||||
}
|
||||
|
||||
private createGraph() {
|
||||
this.oss.arguments.forEach(argument => this.graph.addEdge(argument.argument, argument.operation));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,3 +177,11 @@ export interface GraphFilterParams {
|
|||
allowConstant: boolean;
|
||||
allowTheorem: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents XY Position.
|
||||
*/
|
||||
export interface Position2D {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
|
|
@ -2,18 +2,101 @@
|
|||
* Module: Schema of Synthesis Operations.
|
||||
*/
|
||||
|
||||
import { ILibraryItemData } from './library';
|
||||
import { Graph } from './Graph';
|
||||
import { ILibraryItemData, LibraryItemID } from './library';
|
||||
import { ConstituentaID } from './rsform';
|
||||
|
||||
/**
|
||||
* Represents backend data for Schema of Synthesis Operations.
|
||||
* Represents {@link IOperation} identifier type.
|
||||
*/
|
||||
export type OperationID = number;
|
||||
|
||||
/**
|
||||
* Represents {@link IOperation} type.
|
||||
*/
|
||||
export enum OperationType {
|
||||
INPUT = 'input',
|
||||
SYNTHESIS = 'synthesis'
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents Operation.
|
||||
*/
|
||||
export interface IOperation {
|
||||
id: OperationID;
|
||||
operation_type: OperationType;
|
||||
oss: LibraryItemID;
|
||||
|
||||
alias: string;
|
||||
title: string;
|
||||
comment: string;
|
||||
position_x: number;
|
||||
position_y: number;
|
||||
|
||||
result: LibraryItemID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents {@link IOperation} data, used in creation process.
|
||||
*/
|
||||
export interface IOperationCreateData
|
||||
extends Pick<IOperation, 'alias' | 'operation_type' | 'title' | 'comment' | 'position_x' | 'position_y'> {}
|
||||
|
||||
/**
|
||||
* Represents {@link IOperation} Argument.
|
||||
*/
|
||||
export interface IArgument {
|
||||
operation: OperationID;
|
||||
argument: OperationID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents data, used in merging single {@link IConstituenta}.
|
||||
*/
|
||||
export interface ICstSubstitute {
|
||||
original: ConstituentaID;
|
||||
substitution: ConstituentaID;
|
||||
transfer_term: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents data, used in merging multiple {@link IConstituenta}.
|
||||
*/
|
||||
export interface ICstSubstituteData {
|
||||
substitutions: ICstSubstitute[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents {@link ICstSubstitute} extended data.
|
||||
*/
|
||||
export interface ICstSubstituteEx extends ICstSubstitute {
|
||||
original_alias: string;
|
||||
original_term: string;
|
||||
substitution_alias: string;
|
||||
substitution_term: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents backend data for {@link IOperationSchema}.
|
||||
*/
|
||||
export interface IOperationSchemaData extends ILibraryItemData {
|
||||
additional_data?: number[];
|
||||
items: IOperation[];
|
||||
arguments: IArgument[];
|
||||
substitutions: ICstSubstituteEx[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents Schema of Synthesis Operations.
|
||||
* Represents OperationSchema.
|
||||
*/
|
||||
export interface IOperationSchema extends IOperationSchemaData {
|
||||
producedData: number[]; // TODO: modify this to store calculated state on load
|
||||
graph: Graph;
|
||||
operationByID: Map<OperationID, IOperation>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents data response when creating {@link IOperation}.
|
||||
*/
|
||||
export interface IOperationCreatedResponse {
|
||||
new_operation: IOperation;
|
||||
oss: IOperationSchemaData;
|
||||
}
|
||||
|
|
|
@ -5,10 +5,11 @@
|
|||
import { Graph } from '@/models/Graph';
|
||||
|
||||
import { ILibraryItem, ILibraryItemVersioned, LibraryItemID } from './library';
|
||||
import { ICstSubstitute } from './oss';
|
||||
import { IArgumentInfo, ParsingStatus, ValueClass } from './rslang';
|
||||
|
||||
/**
|
||||
* Represents Constituenta type.
|
||||
* Represents {@link IConstituenta} type.
|
||||
*/
|
||||
export enum CstType {
|
||||
BASE = 'basic',
|
||||
|
@ -21,7 +22,7 @@ export enum CstType {
|
|||
THEOREM = 'theorem'
|
||||
}
|
||||
|
||||
// CstType constant for category dividers in TemplateSchemas. TODO: create separate structure for templates
|
||||
// CstType constant for category dividers in TemplateSchemas
|
||||
export const CATEGORY_CST_TYPE = CstType.THEOREM;
|
||||
|
||||
/**
|
||||
|
@ -30,7 +31,7 @@ export const CATEGORY_CST_TYPE = CstType.THEOREM;
|
|||
export type Position = number;
|
||||
|
||||
/**
|
||||
* Represents {@link Constituenta} identifier type.
|
||||
* Represents {@link IConstituenta} identifier type.
|
||||
*/
|
||||
export type ConstituentaID = number;
|
||||
|
||||
|
@ -124,7 +125,7 @@ export interface IConstituentaList {
|
|||
}
|
||||
|
||||
/**
|
||||
* Represents constituenta data, used in creation process.
|
||||
* Represents {@link IConstituenta} data, used in creation process.
|
||||
*/
|
||||
export interface ICstCreateData
|
||||
extends Pick<
|
||||
|
@ -135,7 +136,7 @@ export interface ICstCreateData
|
|||
}
|
||||
|
||||
/**
|
||||
* Represents data, used in ordering constituents in a list.
|
||||
* Represents data, used in ordering a list of {@link IConstituenta}.
|
||||
*/
|
||||
export interface ICstMovetoData extends IConstituentaList {
|
||||
move_to: Position;
|
||||
|
@ -158,32 +159,6 @@ export interface ICstUpdateData
|
|||
*/
|
||||
export interface ICstRenameData extends ITargetCst, Pick<IConstituentaMeta, 'alias' | 'cst_type'> {}
|
||||
|
||||
/**
|
||||
* Represents data, used in merging single {@link IConstituenta}.
|
||||
*/
|
||||
export interface ICstSubstitute {
|
||||
original: ConstituentaID;
|
||||
substitution: ConstituentaID;
|
||||
transfer_term: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents data, used in merging multiple {@link IConstituenta}.
|
||||
*/
|
||||
export interface ICstSubstituteData {
|
||||
substitutions: ICstSubstitute[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents single substitution for synthesis table.
|
||||
*/
|
||||
export interface ISubstitution {
|
||||
leftCst: IConstituenta;
|
||||
rightCst: IConstituenta;
|
||||
deleteRight: boolean;
|
||||
takeLeftTerm: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents data response when creating {@link IConstituenta}.
|
||||
*/
|
||||
|
@ -265,6 +240,16 @@ export interface IVersionCreatedResponse {
|
|||
schema: IRSFormData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents single substitution for synthesis table.
|
||||
*/
|
||||
export interface ISingleSubstitution {
|
||||
leftCst: IConstituenta;
|
||||
rightCst: IConstituenta;
|
||||
deleteRight: boolean;
|
||||
takeLeftTerm: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents input data for inline synthesis.
|
||||
*/
|
||||
|
|
|
@ -2,19 +2,12 @@
|
|||
|
||||
import { ReactFlowProvider } from 'reactflow';
|
||||
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
import OssFlow from './OssFlow';
|
||||
|
||||
function EditorOssGraph() {
|
||||
const controller = useOssEdit();
|
||||
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<AnimateFade>
|
||||
<OssFlow controller={controller} />
|
||||
</AnimateFade>
|
||||
<OssFlow />
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,49 +1,120 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { NodeTypes, ProOptions, ReactFlow } from 'reactflow';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Edge,
|
||||
EdgeChange,
|
||||
Node,
|
||||
NodeChange,
|
||||
NodeTypes,
|
||||
ProOptions,
|
||||
ReactFlow,
|
||||
useEdgesState,
|
||||
useNodesState,
|
||||
useViewport
|
||||
} from 'reactflow';
|
||||
|
||||
import Overlay from '@/components/ui/Overlay';
|
||||
import AnimateFade from '@/components/wrap/AnimateFade';
|
||||
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
||||
import { useOSS } from '@/context/OssContext';
|
||||
|
||||
import { IOssEditContext } from '../OssEditContext';
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
import InputNode from './InputNode';
|
||||
import OperationNode from './OperationNode';
|
||||
import ToolbarOssGraph from './ToolbarOssGraph';
|
||||
|
||||
const OssNodeTypes: NodeTypes = {
|
||||
synthesis: OperationNode,
|
||||
input: InputNode
|
||||
};
|
||||
|
||||
interface OssFlowProps {
|
||||
controller: IOssEditContext;
|
||||
}
|
||||
|
||||
function OssFlow({ controller }: OssFlowProps) {
|
||||
function OssFlow() {
|
||||
const { calculateHeight } = useConceptOptions();
|
||||
const model = useOSS();
|
||||
const controller = useOssEdit();
|
||||
const viewport = useViewport();
|
||||
|
||||
console.log(model.loading);
|
||||
console.log(controller.isMutable);
|
||||
|
||||
const initialNodes = [
|
||||
{ id: '1', position: { x: 0, y: 0 }, data: { label: '1' }, type: 'input' },
|
||||
{ id: '2', position: { x: 0, y: 100 }, data: { label: '2' }, type: 'synthesis' }
|
||||
];
|
||||
const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];
|
||||
const initialNodes: Node[] = useMemo(
|
||||
() =>
|
||||
!model.schema
|
||||
? []
|
||||
: model.schema.items.map(operation => ({
|
||||
id: String(operation.id),
|
||||
data: { label: operation.alias },
|
||||
position: { x: operation.position_x, y: operation.position_y },
|
||||
type: operation.operation_type.toString()
|
||||
})),
|
||||
[model.schema]
|
||||
);
|
||||
// const initialNodes = [
|
||||
// { id: '1', data: { label: '-' }, position: { x: 100, y: 100 } },
|
||||
// { id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 200 } }
|
||||
// ];
|
||||
|
||||
const initialEdges: Edge[] = useMemo(
|
||||
() =>
|
||||
!model.schema
|
||||
? []
|
||||
: model.schema.arguments.map((argument, index) => ({
|
||||
id: String(index),
|
||||
source: String(argument.argument),
|
||||
target: String(argument.operation)
|
||||
})),
|
||||
[model.schema]
|
||||
);
|
||||
// const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];
|
||||
|
||||
const [nodes, onNodesChange] = useNodesState<Node>(initialNodes);
|
||||
const [edges, onEdgesChange] = useEdgesState<Edge>(initialEdges);
|
||||
|
||||
const handleNodesChange = useCallback(
|
||||
(changes: NodeChange[]) => {
|
||||
// @ts-expect-error TODO: Figure out internal type errors in ReactFlow
|
||||
onNodesChange(changes);
|
||||
},
|
||||
[onNodesChange]
|
||||
);
|
||||
|
||||
const handleEdgesChange = useCallback(
|
||||
(changes: EdgeChange[]) => {
|
||||
// @ts-expect-error TODO: Figure out internal type errors in ReactFlow
|
||||
onEdgesChange(changes);
|
||||
},
|
||||
[onEdgesChange]
|
||||
);
|
||||
|
||||
const handleCreateOperation = useCallback(() => {
|
||||
// TODO: calculate insert location
|
||||
controller.promptCreateOperation(viewport.x, viewport.y);
|
||||
}, [controller, viewport]);
|
||||
|
||||
const proOptions: ProOptions = { hideAttribution: true };
|
||||
|
||||
const canvasWidth = useMemo(() => {
|
||||
return 'calc(100vw - 1rem)';
|
||||
}, []);
|
||||
|
||||
const canvasWidth = useMemo(() => 'calc(100vw - 1rem)', []);
|
||||
const canvasHeight = useMemo(() => calculateHeight('1.75rem + 4px'), [calculateHeight]);
|
||||
|
||||
const OssNodeTypes: NodeTypes = useMemo(
|
||||
() => ({
|
||||
synthesis: OperationNode,
|
||||
input: InputNode
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<AnimateFade>
|
||||
<Overlay position='top-0 pt-1 right-1/2 translate-x-1/2' className='rounded-b-2xl cc-blur'>
|
||||
<ToolbarOssGraph onCreate={handleCreateOperation} />
|
||||
</Overlay>
|
||||
<div className='relative' style={{ height: canvasHeight, width: canvasWidth }}>
|
||||
<ReactFlow nodes={initialNodes} edges={initialEdges} fitView proOptions={proOptions} nodeTypes={OssNodeTypes} />
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={handleEdgesChange}
|
||||
fitView
|
||||
proOptions={proOptions}
|
||||
nodeTypes={OssNodeTypes}
|
||||
/>
|
||||
</div>
|
||||
</AnimateFade>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { IconNewItem } from '@/components/Icons';
|
||||
import BadgeHelp from '@/components/info/BadgeHelp';
|
||||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
|
||||
import { useOssEdit } from '../OssEditContext';
|
||||
|
||||
interface ToolbarOssGraphProps {
|
||||
onCreate: () => void;
|
||||
}
|
||||
|
||||
function ToolbarOssGraph({ onCreate }: ToolbarOssGraphProps) {
|
||||
const controller = useOssEdit();
|
||||
|
||||
return (
|
||||
<div className='cc-icons'>
|
||||
{controller.isMutable ? (
|
||||
<MiniButton
|
||||
title='Новая конституента'
|
||||
icon={<IconNewItem size='1.25rem' className='icon-green' />}
|
||||
disabled={controller.isProcessing}
|
||||
onClick={onCreate}
|
||||
/>
|
||||
) : null}
|
||||
<BadgeHelp
|
||||
topic={HelpTopic.UI_OSS_GRAPH}
|
||||
className={clsx(PARAMETER.TOOLTIP_WIDTH, 'sm:max-w-[40rem]')}
|
||||
offset={4}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ToolbarOssGraph;
|
|
@ -11,7 +11,8 @@ import { useOSS } from '@/context/OssContext';
|
|||
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
||||
import DlgEditEditors from '@/dialogs/DlgEditEditors';
|
||||
import { AccessPolicy } from '@/models/library';
|
||||
import { IOperationSchema } from '@/models/oss';
|
||||
import { Position2D } from '@/models/miscellaneous';
|
||||
import { IOperationCreateData, IOperationSchema } from '@/models/oss';
|
||||
import { UserID, UserLevel } from '@/models/user';
|
||||
import { information } from '@/utils/labels';
|
||||
|
||||
|
@ -28,6 +29,8 @@ export interface IOssEditContext {
|
|||
toggleSubscribe: () => void;
|
||||
|
||||
share: () => void;
|
||||
|
||||
promptCreateOperation: (x: number, y: number) => void;
|
||||
}
|
||||
|
||||
const OssEditContext = createContext<IOssEditContext | null>(null);
|
||||
|
@ -59,6 +62,9 @@ export const OssEditState = ({ children }: OssEditStateProps) => {
|
|||
const [showEditEditors, setShowEditEditors] = useState(false);
|
||||
const [showEditLocation, setShowEditLocation] = useState(false);
|
||||
|
||||
const [showCreateOperation, setShowCreateOperation] = useState(false);
|
||||
const [insertPosition, setInsertPosition] = useState<Position2D>({ x: 0, y: 0 });
|
||||
|
||||
useLayoutEffect(
|
||||
() =>
|
||||
setAccessLevel(prev => {
|
||||
|
@ -136,6 +142,18 @@ export const OssEditState = ({ children }: OssEditStateProps) => {
|
|||
[model]
|
||||
);
|
||||
|
||||
const promptCreateOperation = useCallback((x: number, y: number) => {
|
||||
setInsertPosition({ x: x, y: y });
|
||||
setShowCreateOperation(true);
|
||||
}, []);
|
||||
|
||||
const handleCreateOperation = useCallback(
|
||||
(data: IOperationCreateData) => {
|
||||
model.createOperation(data, operation => toast.success(information.newOperation(operation.alias)));
|
||||
},
|
||||
[model]
|
||||
);
|
||||
|
||||
return (
|
||||
<OssEditContext.Provider
|
||||
value={{
|
||||
|
@ -149,7 +167,9 @@ export const OssEditState = ({ children }: OssEditStateProps) => {
|
|||
promptEditors,
|
||||
promptLocation,
|
||||
|
||||
share
|
||||
share,
|
||||
|
||||
promptCreateOperation
|
||||
}}
|
||||
>
|
||||
{model.schema ? (
|
||||
|
|
|
@ -25,6 +25,7 @@ import DlgRenameCst from '@/dialogs/DlgRenameCst';
|
|||
import DlgSubstituteCst from '@/dialogs/DlgSubstituteCst';
|
||||
import DlgUploadRSForm from '@/dialogs/DlgUploadRSForm';
|
||||
import { AccessPolicy, IVersionData, LocationHead, VersionID } from '@/models/library';
|
||||
import { ICstSubstituteData } from '@/models/oss';
|
||||
import {
|
||||
ConstituentaID,
|
||||
CstType,
|
||||
|
@ -33,7 +34,6 @@ import {
|
|||
ICstCreateData,
|
||||
ICstMovetoData,
|
||||
ICstRenameData,
|
||||
ICstSubstituteData,
|
||||
ICstUpdateData,
|
||||
IInlineSynthesisData,
|
||||
IRSForm,
|
||||
|
|
|
@ -909,8 +909,9 @@ export const information = {
|
|||
|
||||
addedConstituents: (count: number) => `Добавлены конституенты: ${count}`,
|
||||
newLibraryItem: 'Схема успешно создана',
|
||||
newConstituent: (alias: string) => `Конституента добавлена: ${alias}`,
|
||||
newVersion: (version: string) => `Версия создана: ${version}`,
|
||||
newConstituent: (alias: string) => `Конституента добавлена: ${alias}`,
|
||||
newOperation: (alias: string) => `Операция добавлена: ${alias}`,
|
||||
renameComplete: (oldAlias: string, newAlias: string) => `Переименование: ${oldAlias} -> ${newAlias}`,
|
||||
|
||||
versionDestroyed: 'Версия удалена',
|
||||
|
|
Loading…
Reference in New Issue
Block a user