From 18ad3f9f293a7e71ba197f8f55f54be1b0664d20 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Tue, 29 Jul 2025 20:55:29 +0300 Subject: [PATCH] F: Implement crucial constituents UI --- .../apps/rsform/serializers/__init__.py | 1 + .../apps/rsform/serializers/data_access.py | 18 ++ .../apps/rsform/tests/s_views/t_rsforms.py | 13 ++ .../backend/apps/rsform/views/rsforms.py | 31 ++++ rsconcept/frontend/src/components/icons.tsx | 6 +- .../src/features/ai/models/prompting-api.ts | 10 +- .../features/help/items/help-thesaurus.tsx | 6 + .../features/help/items/ui/help-rseditor.tsx | 12 +- .../help/items/ui/help-rsgraph-term.tsx | 17 +- .../src/features/rsform/backend/api.ts | 10 ++ .../features/rsform/backend/rsform-loader.ts | 1 + .../src/features/rsform/backend/types.ts | 8 + .../rsform/backend/use-update-crucial.ts | 25 +++ .../rsform/components/badge-constituenta.tsx | 1 + .../rsform/components/icon-crucial-value.tsx | 10 ++ .../components/icon-dependency-mode.tsx | 6 +- .../rsform/components/info-constituenta.tsx | 3 +- .../components/pick-multi-constituenta.tsx | 3 +- .../rsform/components/rsform-stats.tsx | 7 + .../components/term-graph/graph/tg-node.tsx | 12 +- .../components/toolbar-graph-selection.tsx | 166 +++++++++++++----- .../dlg-create-cst/form-create-cst.tsx | 12 ++ .../dialogs/dlg-edit-cst/form-edit-cst.tsx | 12 ++ .../src/features/rsform/models/rsform.ts | 2 + .../editor-constituenta/form-constituenta.tsx | 33 +++- .../editor-rslist/toolbar-rslist.tsx | 24 +++ .../editor-term-graph/toolbar-term-graph.tsx | 3 +- .../editor-term-graph/view-hidden.tsx | 1 + rsconcept/frontend/src/styling/utilities.css | 4 + 29 files changed, 389 insertions(+), 68 deletions(-) create mode 100644 rsconcept/frontend/src/features/rsform/backend/use-update-crucial.ts create mode 100644 rsconcept/frontend/src/features/rsform/components/icon-crucial-value.tsx diff --git a/rsconcept/backend/apps/rsform/serializers/__init__.py b/rsconcept/backend/apps/rsform/serializers/__init__.py index 966c8f86..bc6e198f 100644 --- a/rsconcept/backend/apps/rsform/serializers/__init__.py +++ b/rsconcept/backend/apps/rsform/serializers/__init__.py @@ -12,6 +12,7 @@ from .basics import ( WordFormSerializer ) from .data_access import ( + CrucialUpdateSerializer, CstCreateSerializer, CstInfoSerializer, CstListSerializer, diff --git a/rsconcept/backend/apps/rsform/serializers/data_access.py b/rsconcept/backend/apps/rsform/serializers/data_access.py index a93074a4..e8c04eeb 100644 --- a/rsconcept/backend/apps/rsform/serializers/data_access.py +++ b/rsconcept/backend/apps/rsform/serializers/data_access.py @@ -72,6 +72,24 @@ class CstUpdateSerializer(StrictSerializer): return attrs +class CrucialUpdateSerializer(StrictSerializer): + ''' Serializer: update crucial status. ''' + target = PKField( + many=True, + queryset=Constituenta.objects.all().only('crucial', 'schema_id') + ) + value = serializers.BooleanField() + + def validate(self, attrs): + schema = cast(LibraryItem, self.context['schema']) + for cst in attrs['target']: + if schema and cst.schema_id != schema.pk: + raise serializers.ValidationError({ + f'{cst.pk}': msg.constituentaNotInRSform(schema.title) + }) + return attrs + + class CstDetailsSerializer(StrictModelSerializer): ''' Serializer: Constituenta data including parse. ''' parse = CstParseSerializer() diff --git a/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py b/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py index bd1a983e..6c68f8aa 100644 --- a/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py +++ b/rsconcept/backend/apps/rsform/tests/s_views/t_rsforms.py @@ -578,6 +578,19 @@ class TestConstituentaAPI(EndpointTester): self.assertEqual(self.cst3.definition_resolved, 'form1') self.assertEqual(self.cst3.term_forms, data['item_data']['term_forms']) + @decl_endpoint('/api/rsforms/{schema}/update-crucial', method='patch') + def test_update_crucial(self): + data = {'target': [self.cst1.pk], 'value': True} + self.executeForbidden(data=data, schema=self.unowned_id) + + self.logout() + self.executeForbidden(data=data, schema=self.owned_id) + + self.login() + self.executeOK(data=data, schema=self.owned_id) + self.cst1.refresh_from_db() + self.assertEqual(self.cst1.crucial, True) + class TestInlineSynthesis(EndpointTester): ''' Testing Operations endpoints. ''' diff --git a/rsconcept/backend/apps/rsform/views/rsforms.py b/rsconcept/backend/apps/rsform/views/rsforms.py index 8cf6b726..ef0592ef 100644 --- a/rsconcept/backend/apps/rsform/views/rsforms.py +++ b/rsconcept/backend/apps/rsform/views/rsforms.py @@ -42,6 +42,7 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr 'load_trs', 'create_cst', 'update_cst', + 'update_crucial', 'move_cst', 'delete_multiple_cst', 'substitute', @@ -137,6 +138,36 @@ class RSFormViewSet(viewsets.GenericViewSet, generics.ListAPIView, generics.Retr data=s.RSFormParseSerializer(schema.model).data ) + @extend_schema( + summary='update crucial attributes of a given list of constituents', + tags=['RSForm'], + request=s.CrucialUpdateSerializer, + responses={ + c.HTTP_200_OK: s.RSFormParseSerializer, + c.HTTP_400_BAD_REQUEST: None, + c.HTTP_403_FORBIDDEN: None, + c.HTTP_404_NOT_FOUND: None + } + ) + @action(detail=True, methods=['patch'], url_path='update-crucial') + def update_crucial(self, request: Request, pk) -> HttpResponse: + ''' Update crucial attributes of a given list of constituents. ''' + model = self._get_item() + serializer = s.CrucialUpdateSerializer(data=request.data, partial=True, context={'schema': model}) + serializer.is_valid(raise_exception=True) + value: bool = serializer.validated_data['value'] + + with transaction.atomic(): + for cst in serializer.validated_data['target']: + cst.crucial = value + cst.save(update_fields=['crucial']) + model.save(update_fields=['time_update']) + + return Response( + status=c.HTTP_200_OK, + data=s.RSFormParseSerializer(model).data + ) + @extend_schema( summary='produce the structure of a given constituenta', tags=['RSForm'], diff --git a/rsconcept/frontend/src/components/icons.tsx b/rsconcept/frontend/src/components/icons.tsx index f242cc80..a9300ff7 100644 --- a/rsconcept/frontend/src/components/icons.tsx +++ b/rsconcept/frontend/src/components/icons.tsx @@ -106,9 +106,9 @@ export { LuDatabase as IconDatabase } from 'react-icons/lu'; export { LuView as IconDBStructure } from 'react-icons/lu'; export { LuPlaneTakeoff as IconRESTapi } from 'react-icons/lu'; export { LuImage as IconImage } from 'react-icons/lu'; -export { PiFediverseLogo as IconGraphSelection } from 'react-icons/pi'; export { GoVersions as IconVersions } from 'react-icons/go'; export { LuAtSign as IconTerm } from 'react-icons/lu'; +export { MdTaskAlt as IconCrucial } from 'react-icons/md'; export { LuSubscript as IconAlias } from 'react-icons/lu'; export { TbMathFunction as IconFormula } from 'react-icons/tb'; export { BiFontFamily as IconText } from 'react-icons/bi'; @@ -150,9 +150,11 @@ export { GrConnect as IconConnect } from 'react-icons/gr'; export { BiPlayCircle as IconExecute } from 'react-icons/bi'; // ======== Graph UI ======= +export { PiFediverseLogo as IconContextSelection } from 'react-icons/pi'; +export { ImMakeGroup as IconGroupSelection } from 'react-icons/im'; export { BiCollapse as IconGraphCollapse } from 'react-icons/bi'; export { BiExpand as IconGraphExpand } from 'react-icons/bi'; -export { LuMaximize as IconGraphMaximize } from 'react-icons/lu'; +export { TiArrowMaximise as IconGraphMaximize } from 'react-icons/ti'; export { BiGitBranch as IconGraphInputs } from 'react-icons/bi'; export { TbEarScan as IconGraphInverse } from 'react-icons/tb'; export { BiGitMerge as IconGraphOutputs } from 'react-icons/bi'; diff --git a/rsconcept/frontend/src/features/ai/models/prompting-api.ts b/rsconcept/frontend/src/features/ai/models/prompting-api.ts index 792b4d26..3c2eb3be 100644 --- a/rsconcept/frontend/src/features/ai/models/prompting-api.ts +++ b/rsconcept/frontend/src/features/ai/models/prompting-api.ts @@ -40,12 +40,20 @@ export function generateSample(target: string): string { export function varSchema(schema: IRSForm): string { let result = `Название концептуальной схемы: ${schema.title}\n`; result += `[${schema.alias}] Описание: "${schema.description}"\n\n`; - result += 'Понятия:\n'; + result += 'Конституенты:\n'; schema.items.forEach(item => { result += `\n${item.alias} - "${labelCstTypification(item)}" - "${item.term_resolved}" - "${ item.definition_formal }" - "${item.definition_resolved}" - "${item.convention}"`; }); + if (schema.stats.count_crucial > 0) { + result += + '\nКлючевые конституенты: ' + + schema.items + .filter(cst => cst.crucial) + .map(cst => cst.alias) + .join(', '); + } return result; } diff --git a/rsconcept/frontend/src/features/help/items/help-thesaurus.tsx b/rsconcept/frontend/src/features/help/items/help-thesaurus.tsx index 85be08f2..45789313 100644 --- a/rsconcept/frontend/src/features/help/items/help-thesaurus.tsx +++ b/rsconcept/frontend/src/features/help/items/help-thesaurus.tsx @@ -1,6 +1,7 @@ import { IconChild, IconConsolidation, + IconCrucial, IconCstAxiom, IconCstBaseSet, IconCstConstSet, @@ -91,6 +92,11 @@ export function HelpThesaurus() { родоструктурной экспликации являются Термин, Конвенция, Типизация (Структура), Формальное определение, Текстовое определение, Комментарий.
+
+
diff --git a/rsconcept/frontend/src/features/rsform/components/pick-multi-constituenta.tsx b/rsconcept/frontend/src/features/rsform/components/pick-multi-constituenta.tsx
index a0cd840c..6056afef 100644
--- a/rsconcept/frontend/src/features/rsform/components/pick-multi-constituenta.tsx
+++ b/rsconcept/frontend/src/features/rsform/components/pick-multi-constituenta.tsx
@@ -116,7 +116,8 @@ export function PickMultiConstituenta({
const cst = schema.cstByID.get(cstID);
return !!cst && isBasicConcept(cst.cst_type);
}}
- isOwned={cstID => !schema.cstByID.get(cstID)?.is_inherited}
+ isCrucial={cstID => schema.cstByID.get(cstID)?.crucial ?? false}
+ isInherited={cstID => schema.cstByID.get(cstID)?.is_inherited ?? false}
value={value}
onChange={onChange}
className='w-fit'
diff --git a/rsconcept/frontend/src/features/rsform/components/rsform-stats.tsx b/rsconcept/frontend/src/features/rsform/components/rsform-stats.tsx
index f756a623..1b89d0ee 100644
--- a/rsconcept/frontend/src/features/rsform/components/rsform-stats.tsx
+++ b/rsconcept/frontend/src/features/rsform/components/rsform-stats.tsx
@@ -1,6 +1,7 @@
import {
IconChild,
IconConvention,
+ IconCrucial,
IconCstAxiom,
IconCstBaseSet,
IconCstConstSet,
@@ -113,6 +114,12 @@ export function RSFormStats({ className, stats }: RSFormStatsProps) {
value={stats.count_theorem}
/>
+