openfair/simulations/
charts.rs

1use crate::{error::Error, Result};
2use serde::{Deserialize, Serialize};
3use serde_json::{json, to_value, Value};
4
5#[derive(Serialize, Deserialize)]
6pub struct D3JsDataControl {
7    id: String,
8    val: f32,
9}
10
11#[derive(Serialize, Deserialize)]
12pub struct D3JsDataThreat {
13    id: String,
14    controls: Vec<D3JsDataControl>,
15}
16
17#[derive(Serialize, Deserialize)]
18pub struct D3JsData {
19    threats: Vec<D3JsDataThreat>,
20}
21
22pub fn scenario_to_d3js(data: &str, t: Vec<&str>) -> Result<Value> {
23    let json: serde_json::Value = serde_json::from_str(data)?;
24    let mut ddd = D3JsData { threats: vec![] };
25    for s in json["simulation_result"]
26        .as_array()
27        .ok_or(Error::SimulationResultNotFound)?
28    {
29        for tt in &t {
30            let mut threat = D3JsDataThreat {
31                id: format!(
32                    "{} {}",
33                    s["threat"].as_str().ok_or(Error::ThreatsFieldNotFound)?,
34                    tt
35                ),
36                controls: vec![],
37            };
38            for c in s["simulation_result"]
39                .as_array()
40                .ok_or(Error::SimulationResultNotFound)?
41            {
42                let control = D3JsDataControl {
43                    id: c["control"]
44                        .as_str()
45                        .ok_or_else(|| {
46                            Error::InvalidChartControl(
47                                c["control"].as_str().unwrap_or_default().to_string(),
48                            )
49                        })?
50                        .to_string(),
51                    val: c[tt]["mean"].as_f64().ok_or_else(|| {
52                        Error::InvalidChartMean(c[tt]["mean"].as_f64().unwrap_or_default())
53                    })? as f32,
54                };
55                threat.controls.push(control);
56            }
57            ddd.threats.push(threat);
58        }
59    }
60    Ok(to_value(ddd)?)
61}
62
63pub fn model_set_to_d3js(data: &str) -> Result<Value> {
64    let mut json: serde_json::Value = serde_json::from_str(data)?;
65    let mut res = json!({
66        "breaf_table": [],
67        "risk_distribution": [],
68        "exceedence_probability_curve": [],
69        "loss_exceedence_curve": [],
70        "component_and_aggregate_risk": [],
71        "dependency_trees": []
72    });
73    //breaf_table
74    for m in json["metamodels"]
75        .as_array()
76        .ok_or(Error::ThreatsFieldNotFound)?
77    {
78        res["breaf_table"]
79            .as_array_mut()
80            .ok_or(Error::ThreatsFieldNotFound)?
81            .push(json!({
82                "name": m["name"],
83                "mean": m["mean"],
84                "min": m["min"],
85                "max": m["max"],
86                "deviation": m["dev"],
87            }));
88    }
89    for m in json["models"]
90        .as_array()
91        .ok_or(Error::ThreatsFieldNotFound)?
92    {
93        res["breaf_table"]
94            .as_array_mut()
95            .ok_or(Error::ThreatsFieldNotFound)?
96            .push(json!({
97                "name": m["name"],
98                "mean": m["mean"],
99                "min": m["min"],
100                "max": m["max"],
101                "deviation": m["dev"],
102            }));
103    }
104    //risk_distribution
105    for m in json["metamodels"]
106        .as_array()
107        .ok_or(Error::ThreatsFieldNotFound)?
108    {
109        let mut v: Vec<Value> = vec![];
110        let mut val: Vec<(f32, f32)> = vec![];
111        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
112        for i in 0..41 {
113            val.push((0.0 + (i as f32) * (vxmax / 40.0), 0.0));
114        }
115        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
116            val[((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 40.0))
117                as usize]
118                .1 += 1.0;
119        }
120
121        for vvv in val {
122            v.push(json!({
123                "x": vvv.0,
124                "y": vvv.1
125            }));
126        }
127        res["risk_distribution"]
128            .as_array_mut()
129            .ok_or(Error::ThreatsFieldNotFound)?
130            .push(json!({
131                "name": m["name"],
132                "values": v
133            }));
134    }
135    for m in json["models"]
136        .as_array()
137        .ok_or(Error::ThreatsFieldNotFound)?
138    {
139        let mut v: Vec<Value> = vec![];
140        let mut val: Vec<(f32, f32)> = vec![];
141        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
142        for i in 0..41 {
143            val.push((0.0 + (i as f32) * (vxmax / 40.0), 0.0));
144        }
145        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
146            val[((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 40.0))
147                as usize]
148                .1 += 1.0;
149        }
150
151        for vvv in val {
152            v.push(json!({
153                "x": vvv.0,
154                "y": vvv.1
155            }));
156        }
157        res["risk_distribution"]
158            .as_array_mut()
159            .ok_or(Error::ThreatsFieldNotFound)?
160            .push(json!({
161                "name": m["name"],
162                "values": v
163            }));
164    }
165
166    //exceedence_probability_curve
167    for m in json["metamodels"]
168        .as_array()
169        .ok_or(Error::ThreatsFieldNotFound)?
170    {
171        let mut v: Vec<Value> = vec![];
172        let mut val: Vec<(f32, f32)> = vec![];
173        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
174        for i in 0..101 {
175            val.push((0.0, 0.0 + (i as f32) * (vxmax / 40.0)));
176        }
177        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
178            for i in (((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 100.0))
179                as u32)..101
180            {
181                val[i as usize].0 += 1.0;
182            }
183        }
184        for vvv in val {
185            v.push(json!({
186                "x": vvv.0,
187                "y": vvv.1
188            }));
189        }
190        res["exceedence_probability_curve"]
191            .as_array_mut()
192            .ok_or(Error::ThreatsFieldNotFound)?
193            .push(json!({
194                "name": m["name"],
195                "values": v
196            }));
197    }
198    for m in json["models"]
199        .as_array()
200        .ok_or(Error::ThreatsFieldNotFound)?
201    {
202        let mut v: Vec<Value> = vec![];
203        let mut val: Vec<(f32, f32)> = vec![];
204        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
205        for i in 0..101 {
206            val.push((0.0, 0.0 + (i as f32) * (vxmax / 40.0)));
207        }
208        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
209            for i in (((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32) / (vxmax / 100.0))
210                as u32)..101
211            {
212                val[i as usize].0 += 1.0;
213            }
214        }
215        for vvv in val {
216            v.push(json!({
217                "x": vvv.0,
218                "y": vvv.1
219            }));
220        }
221        res["exceedence_probability_curve"]
222            .as_array_mut()
223            .ok_or(Error::ThreatsFieldNotFound)?
224            .push(json!({
225                "name": m["name"],
226                "values": v
227            }));
228    }
229
230    //loss_exceedence_curve
231    for m in json["metamodels"]
232        .as_array()
233        .ok_or(Error::ThreatsFieldNotFound)?
234    {
235        let mut v: Vec<Value> = vec![];
236        let mut val: Vec<(f32, f32)> = vec![];
237        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
238        for i in 0..101 {
239            val.push((0.0 + (i as f32) * (vxmax / 100.0), 0.0));
240        }
241        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
242            for i in 0..(((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32)
243                / (vxmax / 100.0)) as u32)
244            {
245                val[i as usize].1 += 1.0;
246            }
247        }
248        for vvv in val {
249            v.push(json!({
250                "x": vvv.0,
251                "y": vvv.1
252            }));
253        }
254        res["loss_exceedence_curve"]
255            .as_array_mut()
256            .ok_or(Error::ThreatsFieldNotFound)?
257            .push(json!({
258                "name": m["name"],
259                "values": v
260            }));
261    }
262    for m in json["models"]
263        .as_array()
264        .ok_or(Error::ThreatsFieldNotFound)?
265    {
266        let mut v: Vec<Value> = vec![];
267        let mut val: Vec<(f32, f32)> = vec![];
268        let vxmax = m["max"].as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32;
269        for i in 0..101 {
270            val.push((0.0 + (i as f32) * (vxmax / 100.0), 0.0));
271        }
272        for vval in m["values"].as_array().ok_or(Error::ThreatsFieldNotFound)? {
273            for i in 0..(((vval.as_f64().ok_or(Error::ThreatsFieldNotFound)? as f32)
274                / (vxmax / 100.0)) as u32)
275            {
276                val[i as usize].1 += 1.0;
277            }
278        }
279        for vvv in val {
280            v.push(json!({
281                "x": vvv.0,
282                "y": vvv.1
283            }));
284        }
285        res["loss_exceedence_curve"]
286            .as_array_mut()
287            .ok_or(Error::ThreatsFieldNotFound)?
288            .push(json!({
289                "name": m["name"],
290                "values": v
291            }));
292    }
293
294    //component_and_aggregate_risk
295    for m in json["metamodels"]
296        .as_array()
297        .ok_or(Error::ThreatsFieldNotFound)?
298    {
299        res["component_and_aggregate_risk"]
300            .as_array_mut()
301            .ok_or(Error::ThreatsFieldNotFound)?
302            .push(json!({
303                "name": m["name"],
304                "mean": m["mean"],
305                "min": m["min"],
306                "max": m["max"],
307                "deviation": m["dev"],
308            }));
309    }
310    for m in json["models"]
311        .as_array()
312        .ok_or(Error::ThreatsFieldNotFound)?
313    {
314        res["component_and_aggregate_risk"]
315            .as_array_mut()
316            .ok_or(Error::ThreatsFieldNotFound)?
317            .push(json!({
318                "name": m["name"],
319                "mean": m["mean"],
320                "min": m["min"],
321                "max": m["max"],
322                "deviation": m["dev"],
323            }));
324    }
325
326    //dependency_trees
327    for m in json["models"]
328        .as_array_mut()
329        .ok_or(Error::ThreatsFieldNotFound)?
330    {
331        clean_json_tree(&mut m["tree"]["root"])?;
332        res["dependency_trees"]
333            .as_array_mut()
334            .ok_or(Error::ThreatsFieldNotFound)?
335            .push(json!({
336                "name": m["name"],
337                "tree": [m["tree"]["root"]]
338            }));
339    }
340    Ok(res)
341}
342
343fn clean_json_tree(v: &mut Value) -> Result<()> {
344    let obj = v.as_object_mut().ok_or(Error::ThreatsFieldNotFound)?;
345    obj.remove("values").ok_or(Error::ThreatsFieldNotFound)?;
346    obj.remove("min").ok_or(Error::ThreatsFieldNotFound)?;
347    obj.remove("max").ok_or(Error::ThreatsFieldNotFound)?;
348    obj.remove("mean").ok_or(Error::ThreatsFieldNotFound)?;
349    obj.remove("dev").ok_or(Error::ThreatsFieldNotFound)?;
350    obj.remove("gen_params")
351        .ok_or(Error::ThreatsFieldNotFound)?;
352    for c in obj["children"]
353        .as_array_mut()
354        .ok_or(Error::ThreatsFieldNotFound)?
355    {
356        clean_json_tree(c)?
357    }
358    Ok(())
359}