Coverage for backend/django/core/auxiliary/views/UploadMSSData.py: 87%
73 statements
« prev ^ index » next coverage.py v7.10.7, created at 2026-06-23 21:51 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2026-06-23 21:51 +0000
1from rest_framework.response import Response
2from django.db import transaction
3from core.auxiliary.models.DataCell import DataCell
4from core.auxiliary.models.DataRow import DataRow
5from drf_spectacular.utils import extend_schema
6from core.auxiliary.models.DataColumn import DataColumn
7from core.auxiliary.models.Scenario import Scenario, ScenarioInputModeEnum
8from core.auxiliary.services.parameter_sweep import clear_mss_input_data
9from rest_framework.decorators import api_view
10from rest_framework import serializers, status
11from core.validation import api_view_validate
12from core.managers import get_flowsheet_access
14class UploadDataSerializer(serializers.Serializer):
15 # THe data format is e.g:
16 # {
17 # "data": {
18 # "heater_enthalpy": [1, 2, 3, 4, 5],
19 # "heater_temperature": [1, 2, 3, 4, 5]
20 # }
21 # "flowsheet": 1
22 # }
23 flowsheet = serializers.IntegerField()
24 scenario=serializers.IntegerField()
25 data = serializers.DictField( # column name
26 child=serializers.ListField( # List of values
27 child=serializers.FloatField() # Value
28 )
29 )
32@api_view_validate
33@extend_schema(request=UploadDataSerializer, responses=None)
34@api_view(['POST'])
35def upload_data(request) -> Response:
36 flowsheet_id = request.GET.get("flowsheet")
37 access_state = get_flowsheet_access(request.user, flowsheet_id)
38 if access_state.has_read_access and not access_state.has_write_access:
39 return Response(
40 {"error": "This flowsheet is shared with read-only access."},
41 status=status.HTTP_403_FORBIDDEN,
42 )
44 try:
45 serializer = UploadDataSerializer(data=request.data)
46 serializer.is_valid(raise_exception=True)
47 validated_data = serializer.validated_data
48 data = validated_data.get('data')
49 flowsheet_id = validated_data.get('flowsheet')
50 scenario_id = validated_data.get('scenario')
51 except Exception as e:
52 return Response(status=400, data=f"Invalid csv data: {e}")
54 if not data: 54 ↛ 55line 54 didn't jump to line 55 because the condition on line 54 was never true
55 return Response(status=400, data="CSV data must include at least one column.")
57 num_rows = len(list(next(iter(data.values()))))
58 if any(len(values) != num_rows for values in data.values()):
59 return Response(status=400, data="CSV columns must all have the same number of rows.")
61 try:
62 scenario = Scenario.objects.get(id=scenario_id, flowsheet_id=flowsheet_id)
63 except Scenario.DoesNotExist:
64 return Response(status=404, data="Scenario not found.")
66 with transaction.atomic():
67 replacing_parameter_sweep = (
68 scenario.mss_input_mode == ScenarioInputModeEnum.ParameterSweep
69 or hasattr(scenario, "parameterSweepDefinition")
70 )
71 if replacing_parameter_sweep: 71 ↛ 72line 71 didn't jump to line 72 because the condition on line 71 was never true
72 clear_mss_input_data(scenario)
74 scenario.mss_input_mode = ScenarioInputModeEnum.Csv
75 scenario.save(update_fields=["mss_input_mode"])
77 # Step 1: Create any missing data column under the given optimization
78 data_columns = []
79 for key in data:
80 data_columns.append(DataColumn(name=key, scenario_id=scenario_id,flowsheet_id=flowsheet_id))
82 DataColumn.objects.bulk_create(data_columns, ignore_conflicts=True)
84 # Step 2: Get existing data rows by optimization
85 existing_rows = list(DataRow.objects.filter(scenario_id=scenario_id).order_by("index"))
86 existing_indices = {r.index for r in existing_rows}
88 # Step 3: Create missing data rows
89 new_rows = [
90 DataRow(index=i, flowsheet_id=flowsheet_id, scenario_id=scenario_id)
91 for i in range(num_rows)
92 if i not in existing_indices
93 ]
94 if new_rows: 94 ↛ 98line 94 didn't jump to line 98 because the condition on line 94 was always true
95 DataRow.objects.bulk_create(new_rows)
97 # Refresh data row list
98 data_rows = list(DataRow.objects.filter(scenario_id=scenario_id).order_by("index"))
99 data_row_map = {r.index: r for r in data_rows}
101 # Step 4: Get updated data columns
102 data_columns = DataColumn.objects.filter(scenario_id=scenario_id).prefetch_related("dataCells")
103 column_map = {column.name: column for column in data_columns}
105 # Step 5: Build mapping of existing values
106 existing_values = {
107 column.name: {sv.data_row.index: sv for sv in column.dataCells.all()}
108 for column in data_columns
109 }
111 # Step 6: Insert or update DataCells
112 for i in range(num_rows):
113 data_row = data_row_map[i]
114 for column_name, values in data.items():
115 value = values[i]
116 column = column_map[column_name]
117 existing = existing_values.get(column_name, {}).get(i)
119 if existing: 119 ↛ 120line 119 didn't jump to line 120 because the condition on line 119 was never true
120 existing.value = value
121 existing.save()
122 else:
123 DataCell.objects.create(value=value, data_column=column, data_row=data_row, flowsheet_id=flowsheet_id)
125 return Response(status=200, data="Data uploaded successfully")