Coverage for backend/django/core/auxiliary/viewsets/FlowsheetTemplateViewSet.py: 90%
102 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-05-13 02:47 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-05-13 02:47 +0000
1from django.db import transaction
2from rest_framework import status, viewsets
3from rest_framework.decorators import action
4from rest_framework.response import Response
5from core.auxiliary.enums.FlowsheetTemplateType import FlowsheetTemplateType
6from core.auxiliary.models.Flowsheet import Flowsheet
7from core.auxiliary.serializers.FlowsheetSerializer import FlowsheetSerializer
8from flowsheetInternals.graphicData.models.groupingModel import Grouping
9from core.auxiliary.methods.CopyFlowsheet import copy_flowsheet_data, create_module_from_template_logic
10from flowsheetInternals.graphicData.serializers.groupingSerializer import GroupingSerializer
11from drf_spectacular.utils import extend_schema
12from rest_framework import serializers
13from core.managers import get_flowsheet_access
16class CreateFlowsheetTemplateSerializer(serializers.Serializer):
17 flowsheet_id = serializers.IntegerField()
18 flowsheet_template_type = serializers.ChoiceField(choices=FlowsheetTemplateType.choices)
21class CreateModuleFromTemplateSerializer(serializers.Serializer):
22 current_group = serializers.IntegerField(required=False, allow_null=True)
23 flowsheet = serializers.IntegerField()
24 x = serializers.FloatField(required=False, allow_null=True)
25 y = serializers.FloatField(required=False, allow_null=True)
28class FlowsheetTemplateViewSet(viewsets.ReadOnlyModelViewSet):
29 serializer_class = FlowsheetSerializer
31 def get_queryset(self):
32 # Public templates are visible to all users
33 # Private templates only to the owner
34 flowsheets = Flowsheet.with_user_access(
35 Flowsheet.objects.select_related('owner'),
36 self.request.user,
37 )
39 public_flowsheet_templates = flowsheets.filter(
40 flowsheet_template_type=FlowsheetTemplateType.PublicTemplate)
41 private_flowsheet_templates = flowsheets.filter(
42 flowsheet_template_type=FlowsheetTemplateType.PrivateTemplate,
43 owner=self.request.user
44 )
45 return public_flowsheet_templates | private_flowsheet_templates
47 @extend_schema(request=CreateFlowsheetTemplateSerializer, responses=None)
48 @action(detail=False, methods=['post'], url_path='create-flowsheet-template')
49 def create_flowsheet_template(self, request) -> Response:
50 """
51 Convert a flowsheet to a template
52 """
53 serializer = CreateFlowsheetTemplateSerializer(data=request.data)
54 serializer.is_valid(raise_exception=True)
56 flowsheet_id = serializer.validated_data['flowsheet_id']
57 flowsheet_template_type = serializer.validated_data['flowsheet_template_type']
59 # Only allow conversion to public template if user is staff
60 if flowsheet_template_type == FlowsheetTemplateType.PublicTemplate and not request.user.is_staff:
61 return Response({'error': 'You do not have permission to create public templates'},
62 status=status.HTTP_403_FORBIDDEN)
64 try:
65 # get the source flowsheet (only owner can convert to template)
66 source_flowsheet = Flowsheet.objects.select_related('owner').get(id=flowsheet_id, owner=request.user)
68 # update the template type
69 source_flowsheet.flowsheet_template_type = flowsheet_template_type
70 source_flowsheet.save(update_fields=['flowsheet_template_type'])
72 return Response(
73 FlowsheetSerializer(source_flowsheet, context={"request": request}).data,
74 status=status.HTTP_200_OK,
75 )
77 except Flowsheet.DoesNotExist:
78 return Response({'error': 'Flowsheet not found or you do not have permission'},
79 status=status.HTTP_404_NOT_FOUND)
81 @extend_schema(responses=None)
82 @action(detail=True, methods=['post'], url_path='create-from-flowsheet-template')
83 def create_from_flowsheet_template(self, request, pk=None) -> Response:
84 """
85 Create a new flowsheet from a template
86 """
87 try:
88 flowsheet_template = self.get_object()
90 # copy the template to create a new flowsheet
91 new_flowsheet = copy_flowsheet_data(flowsheet_template, user=request.user)
93 # reset new flowsheet's template type to regular flowsheet
94 new_flowsheet.flowsheet_template_type = FlowsheetTemplateType.NotTemplate
95 new_flowsheet.save()
97 return Response(
98 FlowsheetSerializer(new_flowsheet, context={"request": request}).data,
99 status=status.HTTP_201_CREATED,
100 )
102 except Flowsheet.DoesNotExist:
103 return Response({'error': 'Template not found'}, status=status.HTTP_404_NOT_FOUND)
104 except Exception as e:
105 return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
107 @extend_schema(request=CreateModuleFromTemplateSerializer, responses=None)
108 @action(detail=True, methods=['post'], url_path='create-module-from-template')
109 def create_module_from_template(self, request, pk=None) -> Response:
110 """
111 Create a new module (grouping) from a template within the current flowsheet
112 """
113 data = request.data.get('createModuleFromTemplate', request.data)
115 serializer = CreateModuleFromTemplateSerializer(data=data)
116 serializer.is_valid(raise_exception=True)
118 current_flowsheet_id = serializer.validated_data['flowsheet']
119 current_group_id = serializer.validated_data.get('current_group')
120 x = serializer.validated_data.get("x")
121 y = serializer.validated_data.get("y")
123 try:
124 with transaction.atomic():
125 flowsheet_template = self.get_object()
126 access_state = get_flowsheet_access(request.user, current_flowsheet_id)
127 if not access_state.has_read_access:
128 return Response({'error': 'Template or flowsheet not found'}, status=status.HTTP_404_NOT_FOUND)
129 if not access_state.has_write_access:
130 return Response(
131 {'error': 'You do not have write access to this flowsheet'},
132 status=status.HTTP_403_FORBIDDEN,
133 )
135 current_flowsheet = Flowsheet.objects.get(id=current_flowsheet_id)
137 current_group: Grouping | None = None
138 if current_group_id: 138 ↛ 142line 138 didn't jump to line 142 because the condition on line 138 was always true
139 current_group = Grouping.objects.get(id=current_group_id)
141 # Create the module from template
142 new_module = create_module_from_template_logic(
143 flowsheet_template,
144 current_flowsheet,
145 request.user,
146 current_group,
147 x,
148 y
149 )
151 return Response(GroupingSerializer(new_module).data, status=status.HTTP_201_CREATED)
153 except Flowsheet.DoesNotExist:
154 return Response({'error': 'Template or flowsheet not found'}, status=status.HTTP_404_NOT_FOUND)
155 except Exception as e:
156 return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)
158 @extend_schema(request=None, responses=FlowsheetSerializer)
159 @action(detail=True, methods=['post'], url_path='revert-flowsheet-template')
160 def revert_flowsheet_template(self, request, pk=None) -> Response:
161 """
162 Revert a template flowsheet back to a regular (not template) flowsheet.
163 """
164 try:
165 flowsheet_template: Flowsheet = self.get_object()
167 # Must currently be a template
168 if flowsheet_template.flowsheet_template_type == FlowsheetTemplateType.NotTemplate: 168 ↛ 169line 168 didn't jump to line 169 because the condition on line 168 was never true
169 return Response({'error': 'Flowsheet is not a template'}, status=status.HTTP_400_BAD_REQUEST)
171 # Permission checks
172 if flowsheet_template.flowsheet_template_type == FlowsheetTemplateType.PublicTemplate:
173 if not request.user.is_staff:
174 return Response(
175 {'error': 'You do not have permission to revert public templates'},
176 status=status.HTTP_403_FORBIDDEN
177 )
179 # Revert to regular flowsheet
180 flowsheet_template.flowsheet_template_type = FlowsheetTemplateType.NotTemplate
181 flowsheet_template.save(update_fields=['flowsheet_template_type'])
183 return Response(
184 FlowsheetSerializer(flowsheet_template, context={"request": request}).data,
185 status=status.HTTP_200_OK,
186 )
188 except Flowsheet.DoesNotExist:
189 return Response({'error': 'Template not found or you do not have permission'},
190 status=status.HTTP_404_NOT_FOUND)
191 except Exception as e:
192 return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)