mirror of
https://github.com/IRBorisov/ConceptPortal.git
synced 2025-06-26 04:50:36 +03:00
F: Add function for predecessor tracing
This commit is contained in:
parent
10a4140c95
commit
077b9d2216
|
@ -10,4 +10,4 @@ from .data_access import (
|
|||
OperationUpdateSerializer,
|
||||
SetOperationInputSerializer
|
||||
)
|
||||
from .responses import NewOperationResponse, NewSchemaResponse
|
||||
from .responses import ConstituentaReferenceResponse, NewOperationResponse, NewSchemaResponse
|
||||
|
|
|
@ -16,3 +16,9 @@ class NewSchemaResponse(serializers.Serializer):
|
|||
''' Serializer: Create RSForm for input operation response. '''
|
||||
new_schema = LibraryItemSerializer()
|
||||
oss = OperationSchemaSerializer()
|
||||
|
||||
|
||||
class ConstituentaReferenceResponse(serializers.Serializer):
|
||||
''' Serializer: Constituenta reference. '''
|
||||
id = serializers.IntegerField()
|
||||
schema = serializers.IntegerField()
|
||||
|
|
|
@ -12,6 +12,8 @@ from rest_framework.response import Response
|
|||
|
||||
from apps.library.models import LibraryItem, LibraryItemType
|
||||
from apps.library.serializers import LibraryItemSerializer
|
||||
from apps.rsform.models import Constituenta
|
||||
from apps.rsform.serializers import CstTargetSerializer
|
||||
from shared import messages as msg
|
||||
from shared import permissions
|
||||
|
||||
|
@ -43,6 +45,8 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
permission_list = [permissions.ItemEditor]
|
||||
elif self.action in ['details']:
|
||||
permission_list = [permissions.ItemAnyone]
|
||||
elif self.action in ['get_predecessor']:
|
||||
permission_list = [permissions.Anyone]
|
||||
else:
|
||||
permission_list = [permissions.Anyone]
|
||||
return [permission() for permission in permission_list]
|
||||
|
@ -306,3 +310,34 @@ class OssViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retriev
|
|||
status=c.HTTP_200_OK,
|
||||
data=s.OperationSchemaSerializer(oss.model).data
|
||||
)
|
||||
|
||||
@extend_schema(
|
||||
summary='get predecessor for target constituenta',
|
||||
tags=['OSS'],
|
||||
request=CstTargetSerializer(),
|
||||
responses={
|
||||
c.HTTP_200_OK: s.ConstituentaReferenceResponse,
|
||||
c.HTTP_400_BAD_REQUEST: None,
|
||||
c.HTTP_403_FORBIDDEN: None,
|
||||
c.HTTP_404_NOT_FOUND: None
|
||||
}
|
||||
)
|
||||
@action(detail=False, methods=['post'], url_path='get-predecessor')
|
||||
def get_predecessor(self, request: Request):
|
||||
''' Get predecessor. '''
|
||||
# TODO: add tests for this method
|
||||
serializer = CstTargetSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
cst = cast(Constituenta, serializer.validated_data['target'])
|
||||
inheritance = m.Inheritance.objects.filter(child=cst)
|
||||
while inheritance.exists():
|
||||
cst = cast(m.Inheritance, inheritance.first()).parent
|
||||
inheritance = m.Inheritance.objects.filter(child=cst)
|
||||
|
||||
return Response(
|
||||
status=c.HTTP_200_OK,
|
||||
data={
|
||||
'id': cst.pk,
|
||||
'schema': cst.schema_id
|
||||
}
|
||||
)
|
||||
|
|
|
@ -231,17 +231,13 @@ class CstTargetSerializer(serializers.Serializer):
|
|||
target = PKField(many=False, queryset=Constituenta.objects.all())
|
||||
|
||||
def validate(self, attrs):
|
||||
schema = cast(LibraryItem, self.context['schema'])
|
||||
cst = cast(Constituenta, attrs['target'])
|
||||
if schema and cst.schema_id != schema.pk:
|
||||
raise serializers.ValidationError({
|
||||
f'{cst.pk}': msg.constituentaNotInRSform(schema.title)
|
||||
})
|
||||
if cst.cst_type not in [CstType.FUNCTION, CstType.STRUCTURED, CstType.TERM]:
|
||||
raise serializers.ValidationError({
|
||||
f'{cst.pk}': msg.constituentaNoStructure()
|
||||
})
|
||||
self.instance = cst
|
||||
if 'schema' in self.context:
|
||||
schema = cast(LibraryItem, self.context['schema'])
|
||||
cst = cast(Constituenta, attrs['target'])
|
||||
if schema and cst.schema_id != schema.pk:
|
||||
raise serializers.ValidationError({
|
||||
f'{cst.pk}': msg.constituentaNotInRSform(schema.title)
|
||||
})
|
||||
return attrs
|
||||
|
||||
|
||||
|
|
|
@ -147,6 +147,10 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
|
|||
serializer = s.CstTargetSerializer(data=request.data, context={'schema': schema})
|
||||
serializer.is_valid(raise_exception=True)
|
||||
cst = cast(m.Constituenta, serializer.validated_data['target'])
|
||||
if cst.cst_type not in [m.CstType.FUNCTION, m.CstType.STRUCTURED, m.CstType.TERM]:
|
||||
raise ValidationError({
|
||||
f'{cst.pk}': msg.constituentaNoStructure()
|
||||
})
|
||||
|
||||
schema_details = s.RSFormParseSerializer(schema).data['items']
|
||||
cst_parse = next(item for item in schema_details if item['id'] == cst.pk)['parse']
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
IPositionsData,
|
||||
ITargetOperation
|
||||
} from '@/models/oss';
|
||||
import { IConstituentaReference, ITargetCst } from '@/models/rsform';
|
||||
|
||||
import { AxiosGet, AxiosPatch, AxiosPost, FrontExchange, FrontPull, FrontPush } from './apiTransport';
|
||||
|
||||
|
@ -73,3 +74,10 @@ export function postExecuteOperation(oss: string, request: FrontExchange<ITarget
|
|||
request: request
|
||||
});
|
||||
}
|
||||
|
||||
export function postFindPredecessor(request: FrontExchange<ITargetCst, IConstituentaReference>) {
|
||||
AxiosPost({
|
||||
endpoint: `/api/oss/get-predecessor`,
|
||||
request: request
|
||||
});
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ export { BiDiamond as IconTemplates } from 'react-icons/bi';
|
|||
export { GiHoneycomb as IconOSS } from 'react-icons/gi';
|
||||
export { LuBaby as IconChild } from 'react-icons/lu';
|
||||
export { RiParentLine as IconParent } from 'react-icons/ri';
|
||||
export { TbOld as IconPredecessor } from 'react-icons/tb';
|
||||
export { BiSpa as IconPredecessor } from 'react-icons/bi';
|
||||
export { RiHexagonLine as IconRSForm } from 'react-icons/ri';
|
||||
export { LuArchive as IconArchive } from 'react-icons/lu';
|
||||
export { LuDatabase as IconDatabase } from 'react-icons/lu';
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
postCreateVersion,
|
||||
postSubscribe
|
||||
} from '@/backend/library';
|
||||
import { postFindPredecessor } from '@/backend/oss';
|
||||
import {
|
||||
getTRSFile,
|
||||
patchDeleteConstituenta,
|
||||
|
@ -37,6 +38,7 @@ import {
|
|||
ConstituentaID,
|
||||
IConstituentaList,
|
||||
IConstituentaMeta,
|
||||
IConstituentaReference,
|
||||
ICstCreateData,
|
||||
ICstMovetoData,
|
||||
ICstRenameData,
|
||||
|
@ -89,6 +91,7 @@ interface IRSFormContext {
|
|||
cstUpdate: (data: ICstUpdateData, callback?: DataCallback<IConstituentaMeta>) => void;
|
||||
cstDelete: (data: IConstituentaList, callback?: () => void) => void;
|
||||
cstMoveTo: (data: ICstMovetoData, callback?: () => void) => void;
|
||||
findPredecessor: (data: ITargetCst, callback: (reference: IConstituentaReference) => void) => void;
|
||||
|
||||
versionCreate: (data: IVersionData, callback?: (version: VersionID) => void) => void;
|
||||
versionUpdate: (target: VersionID, data: IVersionData, callback?: () => void) => void;
|
||||
|
@ -526,6 +529,17 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
[itemID, library, setSchema]
|
||||
);
|
||||
|
||||
const findPredecessor = useCallback((data: ITargetCst, callback: (reference: IConstituentaReference) => void) => {
|
||||
setProcessingError(undefined);
|
||||
postFindPredecessor({
|
||||
data: data,
|
||||
showError: true,
|
||||
setLoading: setProcessing,
|
||||
onError: setProcessingError,
|
||||
onSuccess: callback
|
||||
});
|
||||
}, []);
|
||||
|
||||
const versionUpdate = useCallback(
|
||||
(target: number, data: IVersionData, callback?: () => void) => {
|
||||
setProcessingError(undefined);
|
||||
|
@ -638,6 +652,8 @@ export const RSFormState = ({ itemID, versionID, children }: RSFormStateProps) =
|
|||
cstSubstitute,
|
||||
cstDelete,
|
||||
cstMoveTo,
|
||||
findPredecessor,
|
||||
|
||||
versionCreate,
|
||||
versionUpdate,
|
||||
versionDelete,
|
||||
|
|
|
@ -119,6 +119,11 @@ export interface IConstituenta extends IConstituentaData {
|
|||
children_alias: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents {@link IConstituenta} reference.
|
||||
*/
|
||||
export interface IConstituentaReference extends Pick<IConstituentaMeta, 'id' | 'schema'> {}
|
||||
|
||||
/**
|
||||
* Represents Constituenta list.
|
||||
*/
|
||||
|
|
|
@ -80,21 +80,15 @@ function EditorConstituenta({ activeCst, isModified, setIsModified, onOpenEdit }
|
|||
|
||||
return (
|
||||
<div className='overflow-y-auto' style={{ maxHeight: panelHeight }}>
|
||||
{controller.isContentEditable ? (
|
||||
<ToolbarConstituenta
|
||||
disabled={disabled}
|
||||
modified={isModified}
|
||||
showList={showList}
|
||||
onMoveUp={controller.moveUp}
|
||||
onMoveDown={controller.moveDown}
|
||||
onSubmit={initiateSubmit}
|
||||
onReset={() => setToggleReset(prev => !prev)}
|
||||
onDelete={controller.promptDeleteCst}
|
||||
onClone={controller.cloneCst}
|
||||
onCreate={() => controller.createCst(activeCst?.cst_type, false)}
|
||||
onToggleList={() => setShowList(prev => !prev)}
|
||||
/>
|
||||
) : null}
|
||||
<ToolbarConstituenta
|
||||
activeCst={activeCst}
|
||||
disabled={disabled}
|
||||
modified={isModified}
|
||||
showList={showList}
|
||||
onSubmit={initiateSubmit}
|
||||
onReset={() => setToggleReset(prev => !prev)}
|
||||
onToggleList={() => setShowList(prev => !prev)}
|
||||
/>
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className={clsx(
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
IconMoveDown,
|
||||
IconMoveUp,
|
||||
IconNewItem,
|
||||
IconPredecessor,
|
||||
IconReset,
|
||||
IconSave
|
||||
} from '@/components/Icons';
|
||||
|
@ -16,39 +17,31 @@ import MiniSelectorOSS from '@/components/select/MiniSelectorOSS';
|
|||
import MiniButton from '@/components/ui/MiniButton';
|
||||
import Overlay from '@/components/ui/Overlay';
|
||||
import { HelpTopic } from '@/models/miscellaneous';
|
||||
import { IConstituenta } from '@/models/rsform';
|
||||
import { PARAMETER } from '@/utils/constants';
|
||||
import { prepareTooltip, tooltips } from '@/utils/labels';
|
||||
|
||||
import { useRSEdit } from '../RSEditContext';
|
||||
|
||||
interface ToolbarConstituentaProps {
|
||||
activeCst?: IConstituenta;
|
||||
disabled: boolean;
|
||||
modified: boolean;
|
||||
showList: boolean;
|
||||
|
||||
onSubmit: () => void;
|
||||
onReset: () => void;
|
||||
|
||||
onMoveUp: () => void;
|
||||
onMoveDown: () => void;
|
||||
onDelete: () => void;
|
||||
onClone: () => void;
|
||||
onCreate: () => void;
|
||||
onToggleList: () => void;
|
||||
}
|
||||
|
||||
function ToolbarConstituenta({
|
||||
activeCst,
|
||||
disabled,
|
||||
modified,
|
||||
showList,
|
||||
|
||||
onSubmit,
|
||||
onReset,
|
||||
onMoveUp,
|
||||
onMoveDown,
|
||||
onDelete,
|
||||
onClone,
|
||||
onCreate,
|
||||
onToggleList
|
||||
}: ToolbarConstituentaProps) {
|
||||
const controller = useRSEdit();
|
||||
|
@ -61,53 +54,70 @@ function ToolbarConstituenta({
|
|||
onSelect={(event, value) => controller.viewOSS(value.id, event.ctrlKey || event.metaKey)}
|
||||
/>
|
||||
) : null}
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
|
||||
icon={<IconSave size='1.25rem' className='icon-primary' />}
|
||||
disabled={disabled || !modified}
|
||||
onClick={onSubmit}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Сбросить несохраненные изменения'
|
||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||
disabled={disabled || !modified}
|
||||
onClick={onReset}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Создать конституенту после данной'
|
||||
icon={<IconNewItem size={'1.25rem'} className='icon-green' />}
|
||||
disabled={disabled}
|
||||
onClick={onCreate}
|
||||
/>
|
||||
<MiniButton
|
||||
titleHtml={modified ? tooltips.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
||||
icon={<IconClone size='1.25rem' className='icon-green' />}
|
||||
disabled={disabled || modified}
|
||||
onClick={onClone}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Удалить редактируемую конституенту'
|
||||
disabled={disabled || !controller.canDeleteSelected}
|
||||
onClick={onDelete}
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
/>
|
||||
{activeCst && activeCst.is_inherited ? (
|
||||
<MiniButton
|
||||
title='Перейти к исходной конституенте в ОСС'
|
||||
onClick={() => controller.viewPredecessor(activeCst.id)}
|
||||
icon={<IconPredecessor size='1.25rem' className='icon-primary' />}
|
||||
/>
|
||||
) : null}
|
||||
{controller.isContentEditable ? (
|
||||
<>
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Сохранить изменения', 'Ctrl + S')}
|
||||
icon={<IconSave size='1.25rem' className='icon-primary' />}
|
||||
disabled={disabled || !modified}
|
||||
onClick={onSubmit}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Сбросить несохраненные изменения'
|
||||
icon={<IconReset size='1.25rem' className='icon-primary' />}
|
||||
disabled={disabled || !modified}
|
||||
onClick={onReset}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Создать конституенту после данной'
|
||||
icon={<IconNewItem size={'1.25rem'} className='icon-green' />}
|
||||
disabled={disabled}
|
||||
onClick={() => controller.createCst(activeCst?.cst_type, false)}
|
||||
/>
|
||||
<MiniButton
|
||||
titleHtml={modified ? tooltips.unsaved : prepareTooltip('Клонировать конституенту', 'Alt + V')}
|
||||
icon={<IconClone size='1.25rem' className='icon-green' />}
|
||||
disabled={disabled || modified}
|
||||
onClick={controller.cloneCst}
|
||||
/>
|
||||
<MiniButton
|
||||
title='Удалить редактируемую конституенту'
|
||||
disabled={disabled || !controller.canDeleteSelected}
|
||||
onClick={controller.promptDeleteCst}
|
||||
icon={<IconDestroy size='1.25rem' className='icon-red' />}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<MiniButton
|
||||
title='Отображение списка конституент'
|
||||
icon={showList ? <IconList size='1.25rem' className='icon-primary' /> : <IconListOff size='1.25rem' />}
|
||||
onClick={onToggleList}
|
||||
/>
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
|
||||
icon={<IconMoveUp size='1.25rem' className='icon-primary' />}
|
||||
disabled={disabled || modified}
|
||||
onClick={onMoveUp}
|
||||
/>
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
|
||||
icon={<IconMoveDown size='1.25rem' className='icon-primary' />}
|
||||
disabled={disabled || modified}
|
||||
onClick={onMoveDown}
|
||||
/>
|
||||
|
||||
{controller.isContentEditable ? (
|
||||
<>
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Переместить вверх', 'Alt + вверх')}
|
||||
icon={<IconMoveUp size='1.25rem' className='icon-primary' />}
|
||||
disabled={disabled || modified}
|
||||
onClick={controller.moveUp}
|
||||
/>
|
||||
<MiniButton
|
||||
titleHtml={prepareTooltip('Переместить вниз', 'Alt + вниз')}
|
||||
icon={<IconMoveDown size='1.25rem' className='icon-primary' />}
|
||||
disabled={disabled || modified}
|
||||
onClick={controller.moveDown}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
<BadgeHelp topic={HelpTopic.UI_RS_EDITOR} offset={4} className={PARAMETER.TOOLTIP_WIDTH} />
|
||||
</Overlay>
|
||||
);
|
||||
|
|
|
@ -53,6 +53,8 @@ import { EXTEOR_TRS_FILE } from '@/utils/constants';
|
|||
import { information, prompts } from '@/utils/labels';
|
||||
import { promptUnsaved } from '@/utils/utils';
|
||||
|
||||
import { RSTabID } from './RSTabs';
|
||||
|
||||
export interface IRSEditContext {
|
||||
schema?: IRSForm;
|
||||
selected: ConstituentaID[];
|
||||
|
@ -80,6 +82,7 @@ export interface IRSEditContext {
|
|||
|
||||
viewOSS: (target: LibraryItemID, newTab?: boolean) => void;
|
||||
viewVersion: (version?: VersionID, newTab?: boolean) => void;
|
||||
viewPredecessor: (target: ConstituentaID) => void;
|
||||
createVersion: () => void;
|
||||
restoreVersion: () => void;
|
||||
promptEditVersions: () => void;
|
||||
|
@ -202,6 +205,20 @@ export const RSEditState = ({
|
|||
[router, model]
|
||||
);
|
||||
|
||||
const viewPredecessor = useCallback(
|
||||
(target: ConstituentaID) =>
|
||||
model.findPredecessor({ target: target }, reference =>
|
||||
router.push(
|
||||
urls.schema_props({
|
||||
id: reference.schema,
|
||||
active: reference.id,
|
||||
tab: RSTabID.CST_EDIT
|
||||
})
|
||||
)
|
||||
),
|
||||
[router, model]
|
||||
);
|
||||
|
||||
const viewOSS = useCallback(
|
||||
(target: LibraryItemID, newTab?: boolean) => router.push(urls.oss(target), newTab),
|
||||
[router]
|
||||
|
@ -620,6 +637,7 @@ export const RSEditState = ({
|
|||
|
||||
viewOSS,
|
||||
viewVersion,
|
||||
viewPredecessor,
|
||||
createVersion,
|
||||
restoreVersion,
|
||||
promptEditVersions: () => setShowEditVersions(true),
|
||||
|
|
Loading…
Reference in New Issue
Block a user