Commit 67ee206b authored by Yannick Zohou's avatar Yannick Zohou Committed by GROSS-AMBLARD David
Browse files

merge-proj-viz-into-master

parent 1405a181
......@@ -2,3 +2,4 @@ log/log.txt
projects/spipoll/*
.*
config.php
HWlib.php
\ No newline at end of file
......@@ -3,6 +3,7 @@
session_start();
require_once "HWlib.php";
require_once "TaskViewerModel.php";
$manager = new AjaxManager();
......@@ -19,22 +20,36 @@ if (isset($_POST['action'])) {
/**
* This class goal is to compute data to send back to the ajax call
*/
class AjaxManager {
class AjaxManager
{
private $db;
// Constants : node types
private const ARTIFACT_CLASS = 'artifactClass';
private const ARTIFACT = 'artifact';
private const USER = 'user';
// Constants : node prefix
private const ARTIFACT_CLASS_PREFIX = 'artifactClass_';
private const ARTIFACT_PREFIX = 'artifact_';
private const USER_PREFIX = 'user_';
// Constants : edge types
private const EDGE_ARTIFACT_TO_ARTIFACT_CLASS = 'artifact-to-artifactClass';
private const EDGE_ARTIFACT_TO_USER = 'artifact-to-user';
private const EMPTY_PARAMETER = '';
/**
* Class constructor
*/
function __construct() {
function __construct()
{
$this->db = HWdbconnect();
}
/**
* Retrieve the latest enabled task id and send its value through JSON
*/
function getLastestTaskId() {
function getLastestTaskId()
{
if (isset($_POST['task'])) {
$taskId = $_POST['task'];
$stmt = $this->db->prepare("SELECT idtask FROM profile WHERE idtask = (
......@@ -55,6 +70,96 @@ class AjaxManager {
}
}
}
/**
* get_nodes_data
*
* Returns all nodes data required to build the graph.
*/
function getNodesData()
{
$t_data = array();
// ArtifactClass nodes
$stmt = $this->db->prepare("SELECT * FROM ArtifactClass;");
$stmt->execute();
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
while ($line = ($stmt->fetch())) {
$t_data[] = new NodeData(
self::ARTIFACT_CLASS_PREFIX . $line['id'], // id
$line['description'], // label
self::ARTIFACT_CLASS // type
);
}
// Artifact nodes
$stmt = $this->db->prepare("SELECT * FROM Artifact;");
$stmt->execute();
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
?>
\ No newline at end of file
while ($line = ($stmt->fetch())) {
$t_data[] = new NodeData(
self::ARTIFACT_PREFIX . $line['id'], // id
self::EMPTY_PARAMETER, // label
self::ARTIFACT // type
);
}
// Users nodes, retrieve users who answered tasks
$stmt = $this->db->prepare("SELECT * FROM Users WHERE id IN (SELECT user FROM Answer);");
$stmt->execute();
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
while ($line = ($stmt->fetch())) {
$t_data[] = new NodeData(
self::USER_PREFIX . $line['id'], // id
$line['name'], // label
self::USER // type
);
}
echo json_encode($t_data, JSON_FORCE_OBJECT);
}
/**
* get_edges_data
*
* Returns all edges data required to build the graph.
*/
function getEdgesData()
{
$t_data = array();
// Edges from Artifact nodes to ArtifactClass nodes
$stmt = $this->db->prepare("SELECT id, classid FROM Artifact;");
$stmt->execute();
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
while ($line = ($stmt->fetch())) {
$t_data[] = new EdgeData(
self::ARTIFACT_PREFIX . $line['id'], // source
self::ARTIFACT_CLASS_PREFIX . $line['classid'], // target
self::EDGE_ARTIFACT_TO_ARTIFACT_CLASS, // type
self::EMPTY_PARAMETER, // label
self::EMPTY_PARAMETER // state
);
}
// Edges from Artifact nodes to User nodes
$stmt = $this->db->prepare("SELECT DISTINCT answer.artifact, answer.user, artifactclass.description, artifact.state FROM answered JOIN answer ON answered.modified = answer.modified JOIN artifact ON answer.artifact = artifact.id JOIN artifactclass ON artifact.classid = artifactclass.id;");
$stmt->execute();
$result = $stmt->setFetchMode(PDO::FETCH_ASSOC);
while ($line = ($stmt->fetch())) {
$t_data[] = new EdgeData(
self::ARTIFACT_PREFIX . $line['artifact'], // source
self::USER_PREFIX . $line['user'], // target
self::EDGE_ARTIFACT_TO_USER, // type
$line['description'], // label
$line['state'] // stage
);
}
echo json_encode($t_data, JSON_FORCE_OBJECT);
}
}
<?php
require_once("config.php");
/**
* HW library
*
*/
require_once("config.php");
/**
* If global constant DEBUG is true, echoes the parameter string. In any case, log the message.
......
<?php
/**
* Tasks Viewer Model.
*
* @author Projet M2 VIZ
* @see tasks-viewer.php
*/
/**
* Class NodeData.
*/
class NodeData
{
public $id;
public $label;
public $type;
/**
* Class constructor
*/
public function __construct(string $id, string $label, string $type)
{
$this->id = $id;
$this->label = $label;
$this->type = $type;
}
}
/**
* Class EdgeData.
*/
class EdgeData
{
public $source;
public $target;
public $type;
public $label;
public $state;
/**
* Class constructor
*/
public function __construct(string $source, string $target, string $type, string $label, string $state)
{
$this->source = $source;
$this->target = $target;
$this->type = $type;
$this->label = $label;
$this->state = $state;
}
}
......@@ -2,7 +2,6 @@
require_once("wirk.php");
/**
* sugarFree
*
......@@ -104,32 +103,58 @@ function updateonce($conn, $artifact, $artifactid, $count)
$query="select definition from ArtifactClass,Artifact where Artifact.id=$artifactid and Artifact.classid=ArtifactClass.id";
$artifactfile=$conn->query($query)->fetch()['definition'];
$artifact=load_artifact($artifactfile);
$stateinfo=$artifact[$node];
debug("node data: ".HTMLpre(json_encode($stateinfo)));
// checking if we have reached a final node
if (empty($artifact[$node])){
debug("Reaching final state $node");
$conn->query("update Artifact set state='finished' where ID=$artifactid");
return false;
}
//Empty variable to hold guard value if found
$guard = '';
$actions = '';
foreach ($stateinfo as $dest=>$v) {
debug("checking transition $node -> $dest");
debug("node data: ".json_encode($stateinfo[$dest]));
$guard=$artifact[$node][$dest]["guard"];
$guard=sugarFree($guard);
if( isset($artifact[$node][$dest]["guard"]) ){
$guard=$artifact[$node][$dest]["guard"];
$guard=sugarFree($guard);
}
if($guard=="none")
$guard="select true from dual";
if (preg_match("/^task ([0-9]+) is answered/",$guard,$matches)){
$guard="select true from Answered where artifact=CURRENT_ARTIFACT and id=".$matches[1];
}
$guard=str_replace('SESSION_USER', $_SESSION['id'], $guard);
$guard=str_replace("CURRENT_ARTIFACT",$artifactid, $guard);
$actions=str_replace('SESSION_USER', $_SESSION['id'], $artifact[$node][$dest]["actions"]);
if( isset($artifact[$node][$dest]["actions"]) ){
$actions=str_replace('SESSION_USER', $_SESSION['id'], $artifact[$node][$dest]["actions"]);
}
if (isset($_SESSION['skills'])) {
$actions=str_replace('SKILLS', $_SESSION['skills'], $actions);
// TODO but actions is an array?
// TODO what for?
}
//We make sure that guard and actions are not empty before sending a query to activate step
if($guard !=='' && $actions !== ''){
$activated=step($conn, $artifactid, $node, $dest, $guard, $actions);
debug("result:".$activated);
if ($activated) {
return true;
}
$activated=step($conn, $artifactid, $node, $dest, $guard, $actions);
if ($activated) {
return true;
......@@ -138,6 +163,7 @@ function updateonce($conn, $artifact, $artifactid, $count)
debug("no transition from $node");
return false; // no guard is validated
}
}
/**
* Return the json description of an artifact, stored in the artifactclasses directory
......@@ -276,6 +302,7 @@ function step($conn, $artifactid, $from, $to, $guard, $actions)
$conn->commit();
return $guardvalue;
}
/**
* startArtifact
*
......@@ -344,8 +371,3 @@ function pignistic($conn,$artifactid){
$conn->query("insert into Answer(idtask,user,Artifact,value,mass) select MAX(idtask)+1,1,$artifactid,$choice,1 from Answer where Artifact=$artifactid");
}
?>
<?php
/**
* Configuration file
*
*/
// Set DEBUG to true to see plenty of error messages
define("DEBUG",false);
//define("DEBUG",true);
// Location of the log file
define("LOGFILENAME","log/log.txt");
// Add more error messages from PHP
ini_set('display_startup_errors',1);
ini_set('display_errors',1);
error_reporting(-1);
// Mysql parameters
define("SERVERNAME","localhost");
define("USERNAME","headworkadmin");
define("PASSWORD","<your password here>");
define("DATABASE","headwork");
// Wirk connector parameters
define("WIRKAPIUSER","<your WIRK endpoint here>");
define("WIRKAPIPASSWORD","<your WIRK API password here>");
define("WIRKAPPID","853");
define("WIRKQUALITYID","1386");
define("WIRKPROJECTENDPOINT","https://api.wirk.io/v1_0/AppProject");
define("WIRKTASKLINEENDPOINT","https://api.wirk.io/v1_0/TaskLine");
// Bot parameters
define("NBBOTS",10);
?>
.workflow-tittle {
border: 1px solid #6c757d;
background-color:#6c757d;
border-radius: 5px;
}
.div-artifact-title {
height: fit-content;
overflow: auto;
font-size: 15px;
padding-left: 2px;
}
.div-artifact-info {
height: fit-content;
overflow: auto;
padding-left: 2px;
margin-top: 2px;
}
.toggle {
display: none;
height: fit-content;
}
.artifact-infos {
height: 560px;
overflow-y: auto;
}
.tittle-style {
font-weight: bold;
font-family: Times;
}
.info-style {
font-style: italic;
font-family: courier;
font-size: 16px;
}
.div-artifact-title:hover {
transform: translateY(10px) scale(1);
transition: transform 300ms;
}
.div-artifact-info:hover {
transform: translateY(10px) scale(1);
transition: transform 300ms;
}
.target-popper {
display: none;
max-width: 300px;
padding: 1em;
text-align: left;
border-radius: 10px;
border: 1px solid #ccc;
background: #fff;
z-index: 2;
top: -2.5% !important;
left: 2% !important;
}
.target-popper.active tr td:first-child {
font-weight: bold;
color: #17a2b8 !important;
}
.target-popper.active {
display: block;
}
.sugar-show {-webkit-transition: opacity 1s linear; -o-transition: opacity 1s linear; transition: opacity 1s linear;}
.sugar-hidden {opacity: 0; display: none;}
.legend-label {
font-size: 14px;
position: relative;
top: 95%;
left: 58%;
}
.legend-curent {
background-color: #006400;
height:10px;
width:30px;
margin-left: 2px;
display:inline-block;
position: relative;
top: 95%;
left: 58%;
}
.legend-visited {
background-color: #90EE90;
height:10px;
width:30px;
margin-left: 2px;
display:inline-block;
position: relative;
top: 95%;
left: 58%;
}
\ No newline at end of file
......@@ -54,6 +54,20 @@
*/
// TODO here convert into drawArtifact function
require_once("artifacts.php");
//Fonction récursive pour convertir un tableau d'artifact en ligne sugarfree
function recursiveConvertSugarFree(array $data, array &$convertedData = []){
foreach ($data as $key=>$val){
if(is_array( $val)){
$convertedData[$key] = [];
recursiveConvertSugarFree($val, $convertedData[$key]);
}else{
$convertedData[$key] = sugarFree($val);
}
}
return $convertedData;
}
function drawArtifact($conn,$artifactid){
global $VIEW;
......@@ -64,29 +78,42 @@ function drawArtifact($conn,$artifactid){
//$jsonString=htmlentities(json_encode(load_artifact($artifact_file),JSON_PRETTY_PRINT));
//$jsonString=json_encode(load_artifact($artifact_file),JSON_PRETTY_PRINT);
$jsonString=json_encode(load_artifact($artifact_file));
$loadedArtifact = load_artifact($artifact_file);
$jsonString=json_encode($loadedArtifact);
//Convertir le contenu du json en code sugar free au chargement
$jsonStringSugarFree = json_encode(recursiveConvertSugarFree($loadedArtifact));
//$jsonString='{"author":"bob"}';
$VIEW['MAIN'].=<<<EOF
<div class="container">
<div class="row">
<div class="col bg-secondary text-white">
<div id="hw-artefact-info">placeholder
<div class="workflow-tittle">
<div class="row">
<div class="col bg-secondary text-white">
<div id="hw-artefact-info">placeholder
</div>
</div>
</div>
</div>
<div class="row">
<div class="col bg-secondary text-white">
<div id="hw-artefact-author">placeholder
<div class="row">
<div class="col bg-secondary text-white">
<div id="hw-artefact-author">placeholder
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-6">
<div id="cy"></div>
<div>
<label class="legend-label">Current node</label><div class="legend-curent"></div>
<label class="legend-label">Visited node</label><div class="legend-visited"></div>
</div>
</div>
<div class="col-6">
<div id="info">(move your mouse on nodes and arrows for infos)</div>
<div class="row">
<input type="checkbox" id="toggleId" checked data-toggle="toggle" data-on="Sugar" data-off="Sugar free" data-onstyle="success" data-offstyle="danger">
</div>
<div id="info" style="position: relative;top: 5px; left: 20px;">(move your mouse on nodes and arrows for infos)</div>
</div>
</div>
</div>
......@@ -94,11 +121,13 @@ function drawArtifact($conn,$artifactid){
EOF;
$VIEW['MAIN'].="var jsonString=`".$jsonString."`;";
$VIEW['MAIN'].="var jsonStringSugarFree=`".$jsonStringSugarFree."`;";
$VIEW['MAIN'].=<<<'EOF'
var obj;
try {
obj = JSON.parse(jsonString);
objSugarFree = JSON.parse(jsonStringSugarFree);
} catch(e) {
console.log("error"+jsonString);
alert(e); // error in the above string (in this case, yes)!
......@@ -130,6 +159,15 @@ var cy = cytoscape({
'color':"white"
}
},
{
selector: ':selected',
css: {
'background-color': '#17a2b8',
'line-color': '#17a2b8',
'target-arrow-color': '#17a2b8',
'source-arrow-color': '#17a2b8'
}
},
{
selector: 'node[id="node1"]',
style: {
......@@ -141,7 +179,7 @@ var cy = cytoscape({
style: {
'curve-style':'straight',
'line-color': 'black',
'target-arrow-shape': 'triangle',
'target-arrow-shape': 'vee',
'target-arrow-fill':'filled',
'target-arrow-color':'black',
width:3
......@@ -152,30 +190,37 @@ var cy = cytoscape({
});
for (var i in obj){
if (i!="author" && i!="doc"){
if (i!="author" && i!="doc" && i!="nodetip" ){
cy.add({
data: { id: i }
data: {
id: i,
nodetip: obj[i]["nodetip"]
}
});
console.log('node'+i);
//console.log('node'+i);
}
}
for (var i in obj){
if (i!="author" && i!="doc"){
if (i!="author" && i!="doc" && i!="nodetip"){
for (var j in obj[i]){
console.log('edge' + i + '-' +j);
cy.add({
data: { id: i + '-' +j,
source: i,
target: j
}
});
if(j!="nodetip" ){
cy.add({
data: {
id: i + '-' +j,
source: i,
target: j,
guardtip: obj[i][j]["guardtip"],
actiontip: obj[i][j]["actiontip"]
}
});
}
};
}
}
cy.layout({
name: 'breadthfirst',
//roots: "node1",
......@@ -188,46 +233,261 @@ document.getElementById("hw-artefact-info").innerHTML = obj['doc'];
document.getElementById("hw-artefact-author").innerHTML = 'Author: '+obj['author'];
// Popover sur edge et node ---------------------------------------------------------------
function bindPopper(target) {
let tooltipId = `popper-target-${target.id()}`;
let existingTarget = document.getElementById(tooltipId);
if (existingTarget && existingTarget.length !== 0) {
existingTarget.remove();
}
let popper = target.popper({
content: () => {
// create div container