Compare commits

...

3 Commits

Author SHA1 Message Date
Ivan
b3c8b217c0 B: Fix input min-width
Some checks failed
Backend CI / build (3.12) (push) Has been cancelled
Frontend CI / build (22.x) (push) Has been cancelled
2024-08-30 11:03:31 +03:00
Ivan
afe3ec0adb M: Remove expression column from sideview 2024-08-30 09:58:31 +03:00
Ivan
a4e2d2c25f B: Fix error response to invalid file type 2024-08-30 09:42:21 +03:00
8 changed files with 36 additions and 54 deletions

View File

@ -361,6 +361,11 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr
model = self._get_item() model = self._get_item()
load_metadata = input_serializer.validated_data['load_metadata'] load_metadata = input_serializer.validated_data['load_metadata']
data = utility.read_zipped_json(request.FILES['file'].file, utils.EXTEOR_INNER_FILENAME) data = utility.read_zipped_json(request.FILES['file'].file, utils.EXTEOR_INNER_FILENAME)
if data is None:
return Response(
status=c.HTTP_400_BAD_REQUEST,
data={'file': msg.exteorFileCorrupted()}
)
data['id'] = model.pk data['id'] = model.pk
serializer = s.RSFormTRSSerializer( serializer = s.RSFormTRSSerializer(
@ -486,11 +491,17 @@ class TrsImportView(views.APIView):
request=s.FileSerializer, request=s.FileSerializer,
responses={ responses={
c.HTTP_201_CREATED: LibraryItemSerializer, c.HTTP_201_CREATED: LibraryItemSerializer,
c.HTTP_400_BAD_REQUEST: None,
c.HTTP_403_FORBIDDEN: None c.HTTP_403_FORBIDDEN: None
} }
) )
def post(self, request: Request) -> HttpResponse: def post(self, request: Request) -> HttpResponse:
data = utility.read_zipped_json(request.FILES['file'].file, utils.EXTEOR_INNER_FILENAME) data = utility.read_zipped_json(request.FILES['file'].file, utils.EXTEOR_INNER_FILENAME)
if data is None:
return Response(
status=c.HTTP_400_BAD_REQUEST,
data={'file': msg.exteorFileCorrupted()}
)
owner = cast(User, self.request.user) owner = cast(User, self.request.user)
_prepare_rsform_data(data, request, owner) _prepare_rsform_data(data, request, owner)
serializer = s.RSFormTRSSerializer( serializer = s.RSFormTRSSerializer(
@ -526,6 +537,11 @@ def create_rsform(request: Request) -> HttpResponse:
) )
data = utility.read_zipped_json(request.FILES['file'].file, utils.EXTEOR_INNER_FILENAME) data = utility.read_zipped_json(request.FILES['file'].file, utils.EXTEOR_INNER_FILENAME)
if data is None:
return Response(
status=c.HTTP_400_BAD_REQUEST,
data={'file': msg.exteorFileCorrupted()}
)
_prepare_rsform_data(data, request, owner) _prepare_rsform_data(data, request, owner)
serializer_rsform = s.RSFormTRSSerializer(data=data, context={'load_meta': True}) serializer_rsform = s.RSFormTRSSerializer(data=data, context={'load_meta': True})
serializer_rsform.is_valid(raise_exception=True) serializer_rsform.is_valid(raise_exception=True)

View File

@ -14,6 +14,10 @@ def operationNotInOSS(title: str):
return f'Операция не принадлежит ОСС: {title}' return f'Операция не принадлежит ОСС: {title}'
def exteorFileCorrupted():
return 'Файл Экстеор не соответствует ожидаемому формату. Попробуйте сохранить файл в новой версии'
def previousResultMissing(): def previousResultMissing():
return 'Отсутствует результат предыдущей операции' return 'Отсутствует результат предыдущей операции'

View File

@ -1,13 +1,17 @@
''' Utility functions. ''' ''' Utility functions. '''
import json import json
from io import BytesIO from io import BytesIO
from zipfile import ZipFile from typing import Optional
from zipfile import BadZipFile, ZipFile
def read_zipped_json(data, json_filename: str) -> dict: def read_zipped_json(data, json_filename: str) -> Optional[dict]:
''' Read JSON from zipped data ''' ''' Read JSON from zipped data. '''
with ZipFile(data, 'r') as archive: try:
json_data = archive.read(json_filename) with ZipFile(data, 'r') as archive:
json_data = archive.read(json_filename)
except BadZipFile:
return None
result: dict = json.loads(json_data) result: dict = json.loads(json_data)
return result return result

View File

@ -41,7 +41,7 @@ function TextInput({
<input <input
id={id} id={id}
className={clsx( className={clsx(
'py-2', 'min-w-0 py-2',
'leading-tight truncate hover:text-clip', 'leading-tight truncate hover:text-clip',
{ {
'px-3': !noBorder || !disabled, 'px-3': !noBorder || !disabled,

View File

@ -62,7 +62,7 @@ function FormCreateCst({ schema, state, partialUpdate, setValidated }: FormCreat
id='dlg_cst_alias' id='dlg_cst_alias'
dense dense
label='Имя' label='Имя'
className='w-[7rem] mr-8' className='w-[7rem]'
value={state.alias} value={state.alias}
onChange={event => partialUpdate({ alias: event.target.value })} onChange={event => partialUpdate({ alias: event.target.value })}
/> />

View File

@ -46,7 +46,7 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
hideWindow={hideWindow} hideWindow={hideWindow}
canSubmit={validated} canSubmit={validated}
onSubmit={() => onRename(cstData)} onSubmit={() => onRename(cstData)}
className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex justify-center items-center')} className={clsx('w-[30rem]', 'py-6 pr-3 pl-6 flex gap-3 justify-center items-center')}
> >
<SelectSingle <SelectSingle
id='dlg_cst_type' id='dlg_cst_type'
@ -64,7 +64,7 @@ function DlgRenameCst({ hideWindow, initial, onRename }: DlgRenameCstProps) {
id='dlg_cst_alias' id='dlg_cst_alias'
dense dense
label='Имя' label='Имя'
className='w-[7rem] ml-3' className='w-[7rem]'
value={cstData.alias} value={cstData.alias}
onChange={event => updateData({ alias: event.target.value })} onChange={event => updateData({ alias: event.target.value })}
/> />

View File

@ -1,12 +1,11 @@
'use client'; 'use client';
import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useCallback, useLayoutEffect, useMemo } from 'react';
import BadgeConstituenta from '@/components/info/BadgeConstituenta'; import BadgeConstituenta from '@/components/info/BadgeConstituenta';
import DataTable, { createColumnHelper, IConditionalStyle, VisibilityState } from '@/components/ui/DataTable'; import DataTable, { createColumnHelper, IConditionalStyle } from '@/components/ui/DataTable';
import NoData from '@/components/ui/NoData'; import NoData from '@/components/ui/NoData';
import { useConceptOptions } from '@/context/ConceptOptionsContext'; import { useConceptOptions } from '@/context/ConceptOptionsContext';
import useWindowSize from '@/hooks/useWindowSize';
import { ConstituentaID, IConstituenta } from '@/models/rsform'; import { ConstituentaID, IConstituenta } from '@/models/rsform';
import { isMockCst } from '@/models/rsformAPI'; import { isMockCst } from '@/models/rsformAPI';
import { PARAMETER, prefixes } from '@/utils/constants'; import { PARAMETER, prefixes } from '@/utils/constants';
@ -16,7 +15,6 @@ interface TableSideConstituentsProps {
items: IConstituenta[]; items: IConstituenta[];
activeCst?: IConstituenta; activeCst?: IConstituenta;
onOpenEdit: (cstID: ConstituentaID) => void; onOpenEdit: (cstID: ConstituentaID) => void;
denseThreshold?: number;
autoScroll?: boolean; autoScroll?: boolean;
maxHeight: string; maxHeight: string;
} }
@ -28,13 +26,9 @@ function TableSideConstituents({
activeCst, activeCst,
autoScroll = true, autoScroll = true,
onOpenEdit, onOpenEdit,
maxHeight, maxHeight
denseThreshold = 9999
}: TableSideConstituentsProps) { }: TableSideConstituentsProps) {
const { colors } = useConceptOptions(); const { colors } = useConceptOptions();
const windowSize = useWindowSize();
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({ expression: true });
useLayoutEffect(() => { useLayoutEffect(() => {
if (!activeCst) { if (!activeCst) {
@ -54,17 +48,6 @@ function TableSideConstituents({
} }
}, [activeCst, autoScroll]); }, [activeCst, autoScroll]);
useLayoutEffect(() => {
setColumnVisibility(prev => {
const newValue = (windowSize.width ?? 0) >= denseThreshold;
if (newValue === prev.expression) {
return prev;
} else {
return { expression: newValue };
}
});
}, [windowSize, denseThreshold]);
const handleRowClicked = useCallback( const handleRowClicked = useCallback(
(cst: IConstituenta) => { (cst: IConstituenta) => {
if (!isMockCst(cst)) { if (!isMockCst(cst)) {
@ -103,25 +86,6 @@ function TableSideConstituents({
{props.getValue()} {props.getValue()}
</div> </div>
) )
}),
columnHelper.accessor('definition_formal', {
id: 'expression',
header: 'Выражение',
size: 2000,
minSize: 0,
maxSize: 2000,
enableHiding: true,
cell: props => (
<div
style={{
textWrap: 'pretty',
fontSize: 12,
color: isMockCst(props.row.original) ? colors.fgWarning : undefined
}}
>
{props.getValue()}
</div>
)
}) })
], ],
[colors] [colors]
@ -162,8 +126,6 @@ function TableSideConstituents({
conditionalRowStyles={conditionalRowStyles} conditionalRowStyles={conditionalRowStyles}
headPosition='0' headPosition='0'
enableHiding enableHiding
columnVisibility={columnVisibility}
onColumnVisibilityChange={setColumnVisibility}
noDataComponent={ noDataComponent={
<NoData className='min-h-[5rem]'> <NoData className='min-h-[5rem]'>
<p>Список конституент пуст</p> <p>Список конституент пуст</p>

View File

@ -14,9 +14,6 @@ import { animateSideView } from '@/styling/animations';
import ConstituentsSearch from './ConstituentsSearch'; import ConstituentsSearch from './ConstituentsSearch';
import TableSideConstituents from './TableSideConstituents'; import TableSideConstituents from './TableSideConstituents';
// Window width cutoff for expression show
const COLUMN_EXPRESSION_HIDE_THRESHOLD = 1500;
// Window width cutoff for dense search bar // Window width cutoff for dense search bar
const COLUMN_DENSE_SEARCH_THRESHOLD = 1100; const COLUMN_DENSE_SEARCH_THRESHOLD = 1100;
@ -47,7 +44,6 @@ function ViewConstituents({ expression, schema, activeCst, isBottom, onOpenEdit
activeCst={activeCst} activeCst={activeCst}
onOpenEdit={onOpenEdit} onOpenEdit={onOpenEdit}
autoScroll={!isBottom} autoScroll={!isBottom}
denseThreshold={COLUMN_EXPRESSION_HIDE_THRESHOLD}
/> />
), ),
[isBottom, filteredData, activeCst, onOpenEdit, calculateHeight, accessLevel] [isBottom, filteredData, activeCst, onOpenEdit, calculateHeight, accessLevel]