Skip to content

Commit d28d919

Browse files
committed
Parse the sensitivity analysis results to the database
Make sure of the format of the json to avoid possible html injection
1 parent c08a5a2 commit d28d919

File tree

4 files changed

+83
-41
lines changed

4 files changed

+83
-41
lines changed

app/projects/helpers.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,26 @@ def sensitivity_analysis_payload(
5656
}
5757

5858

59-
SA_MVS_TOKEN_SCHEMA = {
59+
SA_RESPONSE_SCHEMA = {
6060
"type": "object",
61-
"required": ["ref_sim_id", "sensitivity_analysis_ids"],
61+
"required": ["server_info", "mvs_version", "id", "status", "results"],
6262
"properties": {
63+
"server_info": {"type": "string"},
64+
"mvs_version": {"type": "string"},
65+
"id": {"type": "string"},
66+
"status": {"type": "string"},
67+
"results": {
68+
"type": "object",
69+
"required": ["reference_simulation_id", "sensitivity_analysis_steps"],
70+
"properties": {
71+
"reference_simulation_id": {"type": "string"},
72+
"sensitivity_analysis_steps": {
73+
"type": "array",
74+
"items": {"type": "object"},
75+
},
76+
},
77+
"additionalProperties": False,
78+
},
6379
"ref_sim_id": {"type": "string"},
6480
"sensitivity_analysis_ids": {"type": "array", "items": {"type": "string"}},
6581
},
@@ -83,7 +99,12 @@ def sa_output_values_schema_generator(output_names):
8399
"value": {
84100
"oneOf": [
85101
{"type": "null"},
86-
{"type": "array", "items": {"type": "number"}},
102+
{
103+
"type": "array",
104+
"items": {
105+
"anyOf": [{"type": "number"}, {"type": "null"}]
106+
},
107+
},
87108
]
88109
},
89110
"path": {

app/projects/models/simulation_models.py

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,20 @@
55
import jsonschema
66
import numpy as np
77
import logging
8+
9+
logger = logging.getLogger(__name__)
810
from django.db import models
911
from django.core.validators import MaxValueValidator, MinValueValidator
1012

1113
from django.utils.translation import gettext_lazy as _
14+
from datetime import datetime
1215

13-
from projects.constants import USER_RATING
16+
from projects.constants import USER_RATING, DONE, PENDING, ERROR
1417
from projects.helpers import (
1518
sensitivity_analysis_payload,
1619
SA_OUPUT_NAMES_SCHEMA,
1720
sa_output_values_schema_generator,
18-
SA_MVS_TOKEN_SCHEMA,
21+
SA_RESPONSE_SCHEMA,
1922
format_scenario_for_mvs,
2023
)
2124

@@ -65,12 +68,6 @@ def set_reference_scenario(self, scenario):
6568
self.scenario = scenario
6669
self.save()
6770

68-
def collect_simulations_tokens(self, server_response):
69-
try:
70-
jsonschema.validate(server_response, SA_MVS_TOKEN_SCHEMA)
71-
except jsonschema.exceptions.ValidationError:
72-
answer = {}
73-
7471
@property
7572
def variable_range(self):
7673
return np.arange(
@@ -93,17 +90,59 @@ def output_names(self):
9390
@property
9491
def output_values(self):
9592
try:
96-
answer = json.loads(self.output_parameters_values)
97-
try:
98-
jsonschema.validate(
99-
answer, sa_output_values_schema_generator(self.output_names)
100-
)
101-
except jsonschema.exceptions.ValidationError:
102-
answer = {}
93+
out_values = json.loads(self.output_parameters_values)
94+
answer = {}
95+
for in_value, sa_step in zip(self.variable_range, out_values):
96+
try:
97+
jsonschema.validate(
98+
sa_step, sa_output_values_schema_generator(self.output_names)
99+
)
100+
answer[in_value] = sa_step
101+
except jsonschema.exceptions.ValidationError:
102+
answer[in_value] = None
103103
except json.decoder.JSONDecodeError:
104104
answer = {}
105105
return answer
106106

107+
def parse_server_response(self, sa_results):
108+
try:
109+
# make sure the response is formatted as expected
110+
jsonschema.validate(sa_results, SA_RESPONSE_SCHEMA)
111+
self.status = sa_results["status"]
112+
self.errors = (
113+
json.dumps(sa_results["results"][ERROR])
114+
if self.status == ERROR
115+
else None
116+
)
117+
if self.status == DONE:
118+
sa_steps = sa_results["results"]["sensitivity_analysis_steps"]
119+
sa_steps_processed = []
120+
# make sure that each step is formatted as expected
121+
for step_idx, sa_step in enumerate(sa_steps):
122+
try:
123+
jsonschema.validate(
124+
sa_step,
125+
sa_output_values_schema_generator(self.output_names),
126+
)
127+
sa_steps_processed.append(sa_step)
128+
except jsonschema.exceptions.ValidationError as e:
129+
logger.error(
130+
f"Could not parse the results of the sensitivity analysis {self.id} for step {step_idx}"
131+
)
132+
sa_steps_processed.append(None)
133+
self.output_parameters_values = json.dumps(sa_steps_processed)
134+
135+
except jsonschema.exceptions.ValidationError as e:
136+
self.status = ERROR
137+
self.output_parameters_values = ""
138+
self.errors = str(e)
139+
140+
self.elapsed_seconds = (datetime.now() - self.start_date).seconds
141+
self.end_date = (
142+
datetime.now() if sa_results["status"] in [ERROR, DONE] else None
143+
)
144+
self.save()
145+
107146
@property
108147
def payload(self):
109148
return sensitivity_analysis_payload(

app/projects/requests.py

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -108,28 +108,12 @@ def fetch_mvs_simulation_results(simulation):
108108
def fetch_mvs_sa_results(simulation):
109109
if simulation.status == PENDING:
110110
response = mvs_sa_check_status(token=simulation.mvs_token)
111-
try:
112-
simulation.status = response["status"]
113-
simulation.errors = (
114-
json.dumps(response["results"][ERROR])
115-
if simulation.status == ERROR
116-
else None
117-
)
118-
# TODO here fetch the results of the reference scenario in a separate Simulation instance
119-
# and parse the
120-
simulation.results = (
121-
response["results"] if simulation.status == DONE else None
122-
)
111+
112+
simulation.parse_server_response(response)
113+
114+
if simulation.status == DONE:
123115
logger.info(f"The simulation {simulation.id} is finished")
124-
except:
125-
simulation.status = ERROR
126-
simulation.results = None
127116

128-
simulation.elapsed_seconds = (datetime.now() - simulation.start_date).seconds
129-
simulation.end_date = (
130-
datetime.now() if response["status"] in [ERROR, DONE] else None
131-
)
132-
simulation.save()
133117
return simulation.status != PENDING
134118

135119

app/projects/views.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -910,9 +910,7 @@ def sensitivity_analysis_create(request, scen_id, sa_id=None, step_id=5):
910910
else:
911911
sa_item = None
912912
sa_status = None
913-
sa_form = SensitivityAnalysisForm(
914-
scen_id=scen_id,
915-
)
913+
sa_form = SensitivityAnalysisForm(scen_id=scen_id)
916914

917915
answer = render(
918916
request,

0 commit comments

Comments
 (0)