diff --git a/visualizer/composer.json b/visualizer/composer.json index e886050..51e75ee 100644 --- a/visualizer/composer.json +++ b/visualizer/composer.json @@ -8,11 +8,13 @@ "ext-ctype": "*", "ext-iconv": "*", "sensio/framework-extra-bundle": "^6.1", + "symfony/asset": "5.3.*", "symfony/console": "5.3.*", "symfony/dotenv": "5.3.*", "symfony/flex": "^1.3.1", "symfony/framework-bundle": "5.3.*", "symfony/maker-bundle": "^1.31", + "symfony/process": "5.3.*", "symfony/runtime": "5.3.*", "symfony/twig-bundle": "5.3.*", "symfony/yaml": "5.3.*", diff --git a/visualizer/composer.lock b/visualizer/composer.lock index 7e61d2c..982a30e 100644 --- a/visualizer/composer.lock +++ b/visualizer/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e6ab63a7664b90c446777a2cda8e8472", + "content-hash": "a6f1e8be917c69af3d0bc780bc7c7502", "packages": [ { "name": "doctrine/annotations", @@ -584,6 +584,79 @@ }, "time": "2021-05-31T10:40:46+00:00" }, + { + "name": "symfony/asset", + "version": "v5.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/asset.git", + "reference": "4c8d354b8931788f2b07953cfe6846e5cda27637" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/asset/zipball/4c8d354b8931788f2b07953cfe6846e5cda27637", + "reference": "4c8d354b8931788f2b07953cfe6846e5cda27637", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1" + }, + "conflict": { + "symfony/http-foundation": "<5.3" + }, + "require-dev": { + "symfony/http-client": "^4.4|^5.0", + "symfony/http-foundation": "^5.3", + "symfony/http-kernel": "^4.4|^5.0" + }, + "suggest": { + "symfony/http-foundation": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Asset\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Manages URL generation and versioning of web assets such as CSS stylesheets, JavaScript files and image files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/asset/tree/v5.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-26T17:43:10+00:00" + }, { "name": "symfony/cache", "version": "v5.3.0", @@ -2605,6 +2678,68 @@ ], "time": "2021-05-21T13:25:03+00:00" }, + { + "name": "symfony/process", + "version": "v5.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "53e36cb1c160505cdaf1ef201501669c4c317191" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/53e36cb1c160505cdaf1ef201501669c4c317191", + "reference": "53e36cb1c160505cdaf1ef201501669c4c317191", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.15" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-05-26T12:52:38+00:00" + }, { "name": "symfony/routing", "version": "v5.3.0", diff --git a/visualizer/public/css/main.css b/visualizer/public/css/main.css new file mode 100644 index 0000000..8ae5e0e --- /dev/null +++ b/visualizer/public/css/main.css @@ -0,0 +1,5 @@ +#mynetwork { + height: 500px; + width: 500px; + background-color: black; +} diff --git a/visualizer/public/graph.dot b/visualizer/public/graph.dot new file mode 100644 index 0000000..2c2915c --- /dev/null +++ b/visualizer/public/graph.dot @@ -0,0 +1,283 @@ +strict graph { +0; +1; +2; +3; +4; +5; +6; +7; +8; +9; +10; +11; +12; +13; +14; +15; +16; +17; +18; +19; +20; +21; +22; +23; +24; +25; +26; +27; +28; +29; +30; +31; +32; +33; +34; +35; +36; +37; +38; +39; +40; +41; +42; +43; +44; +45; +46; +47; +48; +49; +50; +51; +52; +53; +54; +55; +56; +57; +58; +59; +60; +61; +62; +63; +64; +65; +66; +67; +68; +69; +70; +71; +72; +73; +74; +75; +76; +77; +78; +79; +80; +81; +82; +83; +84; +85; +86; +87; +88; +89; +90; +91; +92; +93; +94; +95; +96; +97; +98; +99; +0 -- 10; +0 -- 1; +1 -- 11; +1 -- 2; +2 -- 12; +2 -- 3; +3 -- 13; +3 -- 4; +4 -- 14; +4 -- 5; +5 -- 15; +5 -- 6; +6 -- 16; +6 -- 7; +7 -- 17; +7 -- 8; +8 -- 18; +8 -- 9; +9 -- 19; +10 -- 20; +10 -- 11; +11 -- 21; +11 -- 12; +12 -- 22; +12 -- 13; +13 -- 23; +13 -- 14; +14 -- 24; +14 -- 15; +15 -- 25; +15 -- 16; +16 -- 26; +16 -- 17; +17 -- 27; +17 -- 18; +18 -- 28; +18 -- 19; +19 -- 29; +20 -- 30; +20 -- 21; +21 -- 31; +21 -- 22; +22 -- 32; +22 -- 23; +23 -- 33; +23 -- 24; +24 -- 34; +24 -- 25; +25 -- 35; +25 -- 26; +26 -- 36; +26 -- 27; +27 -- 37; +27 -- 28; +28 -- 38; +28 -- 29; +29 -- 39; +30 -- 40; +30 -- 31; +31 -- 41; +31 -- 32; +32 -- 42; +32 -- 33; +33 -- 43; +33 -- 34; +34 -- 44; +34 -- 35; +35 -- 45; +35 -- 36; +36 -- 46; +36 -- 37; +37 -- 47; +37 -- 38; +38 -- 48; +38 -- 39; +39 -- 49; +40 -- 50; +40 -- 41; +41 -- 51; +41 -- 42; +42 -- 52; +42 -- 43; +43 -- 53; +43 -- 44; +44 -- 54; +44 -- 45; +45 -- 55; +45 -- 46; +46 -- 56; +46 -- 47; +47 -- 57; +47 -- 48; +48 -- 58; +48 -- 49; +49 -- 59; +50 -- 60; +50 -- 51; +51 -- 61; +51 -- 52; +52 -- 62; +52 -- 53; +53 -- 63; +53 -- 54; +54 -- 64; +54 -- 55; +55 -- 65; +55 -- 56; +56 -- 66; +56 -- 57; +57 -- 67; +57 -- 58; +58 -- 68; +58 -- 59; +59 -- 69; +60 -- 70; +60 -- 61; +61 -- 71; +61 -- 62; +62 -- 72; +62 -- 63; +63 -- 73; +63 -- 64; +64 -- 74; +64 -- 65; +65 -- 75; +65 -- 66; +66 -- 76; +66 -- 67; +67 -- 77; +67 -- 68; +68 -- 78; +68 -- 69; +69 -- 79; +70 -- 80; +70 -- 71; +71 -- 81; +71 -- 72; +72 -- 82; +72 -- 73; +73 -- 83; +73 -- 74; +74 -- 84; +74 -- 75; +75 -- 85; +75 -- 76; +76 -- 86; +76 -- 77; +77 -- 87; +77 -- 78; +78 -- 88; +78 -- 79; +79 -- 89; +80 -- 90; +80 -- 81; +81 -- 91; +81 -- 82; +82 -- 92; +82 -- 83; +83 -- 93; +83 -- 84; +84 -- 94; +84 -- 85; +85 -- 95; +85 -- 86; +86 -- 96; +86 -- 87; +87 -- 97; +87 -- 88; +88 -- 98; +88 -- 89; +89 -- 99; +90 -- 91; +91 -- 92; +92 -- 93; +93 -- 94; +94 -- 95; +95 -- 96; +96 -- 97; +97 -- 98; +98 -- 99; +} + diff --git a/visualizer/public/js/main.js b/visualizer/public/js/main.js new file mode 100644 index 0000000..91de467 --- /dev/null +++ b/visualizer/public/js/main.js @@ -0,0 +1,121 @@ + // create an array with nodes + //var nodes = new vis.DataSet([ + //{ id: 1, label: "Node 1" }, + //{ id: 2, label: "Node 2" }, + //{ id: 3, label: "Node 3" }, + //{ id: 4, label: "Node 4" }, + //{ id: 5, label: "Node 5" } + //]); +// + //// create an array with edges + //var edges = new vis.DataSet([ + //{ from: 1, to: 3 }, + //{ from: 1, to: 2 }, + //{ from: 2, to: 4 }, + //{ from: 2, to: 5 }, + //{ from: 3, to: 3 } + //]); + + // create a network + //var container = document.getElementById("mynetwork"); + //var data = { + //nodes: nodes, + //edges: edges + //}; + //var options = { + //height: '100%', + //width: '100%', + //}; + //var network = new vis.Network(container, data, options); +var network; +var simulationData; +function createGraphFromDot(dotString){ + var parsedData = vis.parseDOTNetwork(dotString); + // create a network + var container = document.getElementById("mynetwork"); + var data = { + nodes: parsedData.nodes, + edges: parsedData.edges + }; + var options = { + height: '100%', + width: '100%', + autoResize: true, + nodes:{ + font:{ + size:0 + }, + }, + layout: { + randomSeed: undefined, + improvedLayout:true, + clusterThreshold: 150, + //hierarchical: { + //enabled:false, + //levelSeparation: 150, + //nodeSpacing: 100, + //treeSpacing: 200, + //blockShifting: true, + //edgeMinimization: true, + //parentCentralization: true, + //direction: 'UD', // UD, DU, LR, RL + //sortMethod: 'hubsize', // hubsize, directed + //shakeTowards: 'leaves' // roots, leaves + //} + }, + }; + network = new vis.Network(container, data, options); +//network.on("selectNode", function (params) { + //var selectedNodeId = params.nodes[0]; + //var node = network.body.nodes[selectedNodeId]; + //node.setOptions({ + //font: { + //size: 20 + //} + //}); +//}); + + //console.log(network.selectNodes([0])); + n = network.body.nodes[0]; + n.setOptions({ + color:{ + background: "#ffffff", + } + }) +} + +function visualizeOneStep(step){ + for(i = 0; i < step.length; i++){ + n = network.body.nodes[i]; + //console.log(step[i]) + if(step[i][0] == 0){ + n.setOptions({ + color:{ + background: "#0000ff", + } + }); + }else{ + n.setOptions({ + color:{ + background: "#ff0000", + } + }); + } + } +} + + +function getSimulation(){ + fetch("http://localhost/simulate") + .then(response => response.json()) + .then(response =>{ + simulationData = response; + createGraphFromDot(simulationData.dotGraph); + //kelvin to celsius + //let temp = Math.ceil(response.main.temp - 273.15); + //document.getElementById("weather").innerHTML = temp + "ºC"; + //console.log(response.dotGraph) + }); +} + +getSimulation() diff --git a/visualizer/public/python/graph.dot b/visualizer/public/python/graph.dot new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/visualizer/public/python/graph.dot @@ -0,0 +1 @@ + diff --git a/visualizer/src/python/simulation.py b/visualizer/public/python/simulation.py old mode 100644 new mode 100755 similarity index 62% rename from visualizer/src/python/simulation.py rename to visualizer/public/python/simulation.py index 753aaa8..83e7abe --- a/visualizer/src/python/simulation.py +++ b/visualizer/public/python/simulation.py @@ -1,9 +1,10 @@ -import math, random, os, time +import math, random, os, time, sys, io +import json import numpy as np import networkx as nx import scipy import time -from networkx.drawing.nx_agraph import write_dot +from networkx.drawing.nx_pydot import write_dot def clean_shuffle_graph(G): random_seed_state = int(random.random()*100000) # quick hack to go back to random afterwards @@ -17,10 +18,9 @@ def clean_shuffle_graph(G): random.seed(random_seed_state) return G -def gen_sis(G, inf_rate=1.0, rec_rate=2.0, noise=0.1): +def gen_sis(G, steps = 1000, inf_rate=1.0, rec_rate=2.0, noise=0.1, statesList = None): S = [1., 0.] I = [0., 1.] - steps = 1000 + random.choice(range(1000)) states = [random.choice([S, I]) for i in range(G.number_of_nodes())] for _ in range(steps): rates = np.zeros(G.number_of_nodes()) @@ -34,13 +34,35 @@ def gen_sis(G, inf_rate=1.0, rec_rate=2.0, noise=0.1): jump_time = np.random.exponential(rates) change_n = np.argmin(jump_time) states[change_n] = S if states[change_n] == I else I + statesList.append(states) return states +statesList = [] +steps = 1000 + random.choice(range(1000)) G_grid10x10 = nx.grid_2d_graph(10,10) G = clean_shuffle_graph(G_grid10x10) -TS_data = gen_sis(G) -print(G.number_of_nodes()) -f = open("graph.dot", "w") -write_dot(G, f) -f.write("\n") -f.close() +TS_data = gen_sis(G, steps=steps,statesList=statesList) +# print(G.number_of_nodes()) +# f = open("graph.dot", "rw") +# nx.drawing.nx_pydot.write_dot(G,f) +# f.write("\n") +# f.close() +# Writing the json file to stdout +# f = StringIO("") +dotGraph = "" + +#Write dot file to string +with io.StringIO() as f: + write_dot(G, f) + f.seek(0) + dotGraph = f.read() + +output = { + "name" : "testGraph", + "dotGraph" : dotGraph, + "steps" : steps, + "states" : statesList, + } + +jsonOutput = json.dumps(output) +print(jsonOutput) diff --git a/visualizer/src/Controller/SimulationController.php b/visualizer/src/Controller/SimulationController.php new file mode 100644 index 0000000..b14008d --- /dev/null +++ b/visualizer/src/Controller/SimulationController.php @@ -0,0 +1,37 @@ +render('simulation/index.html.twig', [ + 'controller_name' => 'SimulationController', + ]); + } + #[Route('/simulate', name: 'run_simulation')] + public function simulate(): Response + { + $process1 = new Process(['python3', 'python/simulation.py']); + //$process = new Process(['cat', 'python/graph.dot']); + $process1->run(); + //$process->run(); + + // executes after the command finishes + if (!$process1->isSuccessful()) { + throw new ProcessFailedException($process1); + } + + $output = $process1->getOutput(); + $response = new Response($output); + return $response; + } +} diff --git a/visualizer/symfony.lock b/visualizer/symfony.lock index 1cf8d48..84e8811 100644 --- a/visualizer/symfony.lock +++ b/visualizer/symfony.lock @@ -44,6 +44,9 @@ "config/packages/sensio_framework_extra.yaml" ] }, + "symfony/asset": { + "version": "v5.3.0" + }, "symfony/cache": { "version": "v5.3.0" }, @@ -156,6 +159,9 @@ "symfony/polyfill-php81": { "version": "v1.23.0" }, + "symfony/process": { + "version": "v5.3.0" + }, "symfony/routing": { "version": "5.3", "recipe": { diff --git a/visualizer/templates/base.html.twig b/visualizer/templates/base.html.twig index 16d7273..9974541 100755 --- a/visualizer/templates/base.html.twig +++ b/visualizer/templates/base.html.twig @@ -15,5 +15,7 @@ {% block body %}{% endblock %} + {% block css%}{% endblock %} + {% block js%}{% endblock %} diff --git a/visualizer/templates/main/index.html.twig b/visualizer/templates/main/index.html.twig index c11eb24..a875be4 100755 --- a/visualizer/templates/main/index.html.twig +++ b/visualizer/templates/main/index.html.twig @@ -1,20 +1,18 @@ {% extends 'base.html.twig' %} {% block title %}Hello MainController!{% endblock %} - -{% block body %} - - -
-

Hello {{ controller_name }}! ✅

- - This friendly message is coming from: - -
+{% block css%} + +{% endblock %} +{% block js%} + {# Vis.js plugin #} + + + + +{% endblock %} +{% block body %} + +

Hello {{ controller_name }}! ✅

+
{% endblock %} diff --git a/visualizer/templates/simulation/index.html.twig b/visualizer/templates/simulation/index.html.twig new file mode 100644 index 0000000..66b79a7 --- /dev/null +++ b/visualizer/templates/simulation/index.html.twig @@ -0,0 +1,20 @@ +{% extends 'base.html.twig' %} + +{% block title %}Hello SimulationController!{% endblock %} + +{% block body %} + + +
+

Hello {{ controller_name }}! ✅

+ + This friendly message is coming from: + +
+{% endblock %} diff --git a/visualizer/templates/test/index.html.twig b/visualizer/templates/test/index.html.twig index 2ed2c3c..4ba37e5 100755 --- a/visualizer/templates/test/index.html.twig +++ b/visualizer/templates/test/index.html.twig @@ -1,8 +1,8 @@ {% extends 'base.html.twig' %} {% block title %}Hello TestController!{% endblock %} - {% block body %} +