Gestion de l'export de données formattées pour gephi ou autre logiciel de visualisation de réseaux

This commit is contained in:
xdrm-brackets 2016-06-01 17:06:18 +02:00
parent 4ccd6ac8ed
commit 0de834875a
15 changed files with 12256 additions and 906 deletions

View File

@ -71,20 +71,24 @@
/* [3] Test de la vérification du format de fichier pour l'upload
=========================================================*/
?>
<form action='' method='POST' enctype='multipart/form-data'>
<!-- <form action='' method='POST' enctype='multipart/form-data'>
<input type='file' name='file'>
<input type='submit' value='Upload'>
</form>
</form> -->
<?php
var_dump($_FILES);
// var_dump($_FILES);
//
// if( isset($_FILES) ){
//
// $request = new ModuleRequest('upload/local_data',array() );
// $response = $request->dispatch();
// var_dump( ManagerError::explicit($response->error) );
//
// }
if( isset($_FILES) ){
$request = new ModuleRequest('upload/local_data',array() );
$response = $request->dispatch();
var_dump( ManagerError::explicit($response->error) );
}
$rq = new ModuleRequest('download/chart', array('subjects'=>[1], 'phone'=>true));
$rs = $rq->dispatch();
var_dump($rs);
/* [4] Test download via AJAX

View File

@ -327,6 +327,19 @@
"survey": { "description": "Si vaut TRUE, renvoie les sujets ResTIC.", "type": "boolean", "optional": true },
"all": { "description": "Si vaut TRUE, renvoie tous les sujets enregistrés.", "type": "boolean", "optional": true }
}
},
"chart": {
"description": "Download des données relatives aux sujets donnés sur le principe noeuds+liens.",
"permissions": ["admin"],
"options": { "download": true },
"parameters": {
"subjects": { "description": "Identifiants des sujets d'enquêtes à intégrer.", "type": "array<id>", "optional": true },
"phone": { "description": "Si vaut TRUE, renvoie les sujets cellulaires.", "type": "boolean", "optional": true },
"facebook": { "description": "Si vaut TRUE, renvoie les sujet facebook.", "type": "boolean", "optional": true },
"survey": { "description": "Si vaut TRUE, renvoie les sujets ResTIC.", "type": "boolean", "optional": true },
"all": { "description": "Si vaut TRUE, renvoie tous les sujets enregistrés.", "type": "boolean", "optional": true }
}
}
}
}

View File

@ -339,6 +339,261 @@
);
}
/* EXPORT POUR GEPHI OU AUTRE LOGICIEL SUR LE PRINCIPE NODES+EDGES
*
* @subjects<Array> Liste des identifiants des sujets à prendre en compte
* @phone<Boolean> Si TRUE, prend en compte les données des questionnaires cellulaires
* @facebook<Boolean> Si TRUE, prend en compte les données des questionnaires facebook
* @survey<Boolean> Si TRUE, prend en compte les données des questionnaires ResTIC
* @all<Boolean> Si TRUE, prend en compte tous les sujets (annule @subjects)
*
* @return data<File> Retourne une archive .zip contenant toutes les données sélectionnées
*/
public static function chart($params){
extract($params);
/* (0) Gestion du formattage des paramètres */
$subjects = !is_array($subjects) ? array() : $subjects;
$phone = !is_bool($phone) ? false : $phone;
$facebook = !is_bool($facebook) ? false : $facebook;
$survey = !is_bool($survey) ? false : $survey;
$all = !is_bool($all) ? false : $all;
/* [0] On récupère le dictionnaire
=========================================================*/
$dict = ResourceDispatcher::getResource('f/json/dictionary/dy');
/* (2) Si une erreur pour le fichier de conf */
if( $dict === false )
return array( 'ModuleError' => ManagerError::UnreachableResource );
/* (3) On récupère la config sous forme de tableau */
$dict = json_decode( $dict, true );
/* (4) Si erreur de PARSAGE */
if( !is_array($dict) )
return array( 'ModuleError' => ManagerError::ParsingFailed );
/* [1] On construit l'arborescence des données
=========================================================*/
$output = array(
'common_' => array(
'contacts' => '',
'relations' => '',
'dict' => ''
),
'logs/' => array() // Contiendra les journaux d'appels
);
/* [2] On construit les fichiers de chaque sujet DE TELEPHONE
=========================================================*/
if( $phone ){ // Si @phone vaut TRUE
// On ouvre une instance de la base de données
$db = new lightdb('phone_db', __ROOT__.'/src/dynamic/');
// Si on doit prendre tous les sujets, on les récupère
if( $all )
$subjects = array_keys( $db->index() );
// Pour chaque sujet
foreach($subjects as $s=>$subjectId){
/* (1) On récupère les données du sujet en cours */
$subjectData = $db->fetch($subjectId);
// Si on ne trouve rien, on passe au suivant
if( $subjectData === false )
continue;
/* (2) On construit le log s'il existe */
$output['logs/'][$subjectId] = self::parseCSV($subjectData['logs'], $dict['logs']);
/* (3) On complète les relations */
// {1} On retire les valeurs ou le type = 0 //
$formattedRelations = array();
foreach($subjectData['relations'] as $i=>$relation)
if( $relation['type'] != 0 )
array_push($formattedRelations, array(
'source' => $relation['idA'],
'target' => $relation['idB'],
'weight' => ($relation['idA']==$subjectId) ? .1 : 1 // plus de poids aux relations alter/alter
));
// {2} On ajoute au contenu //
$output['common_']['relations'] .= self::parseCSV($formattedRelations, array(), strlen($output['common_']['relations']) == 0 ); // On affiche les colonnes pour la première fois uniquement
/* (4) On ajoute les contacts à la liste */
$output['common_']['contacts'] .= self::parseCSV($subjectData['contacts'], $dict['contacts'], strlen($output['common_']['contacts']) == 0 ); // On affiche les colonnes pour la première fois uniquement
}
// On ferme l'instance de la base de données
$db->close();
}
/* [3] On construit les fichiers de chaque sujet DE FACEBOOK
=========================================================*/
if( $facebook ){ // Si @facebook vaut TRUE
// On ouvre une instance de la base de données
$db = new lightdb('facebook_db', __ROOT__.'/src/dynamic/');
// Si on doit prendre tous les sujets, on les récupère
if( $all )
$subjects = array_keys( $db->index() );
// Pour chaque sujet
foreach($subjects as $s=>$subjectId){
/* (1) On récupère les données du sujet en cours */
$subjectData = $db->fetch($subjectId);
// Si on ne trouve rien, on passe au suivant
if( $subjectData === false )
continue;
/* (2) On complète les relations */
// {1} On retire les valeurs ou le type = 0 //
$formattedRelations = array();
foreach($subjectData['relations'] as $i=>$relation)
if( $relation['type'] != 0 )
array_push($formattedRelations, array(
'source' => $relation['idA'],
'target' => $relation['idB'],
'weight' => ($relation['idA']==$subjectId) ? .1 : 1 // plus de poids aux relations alter/alter
));
// {2} On ajoute au contenu //
$output['common_']['relations'] .= self::parseCSV($formattedRelations, array(), strlen($output['common_']['relations']) == 0 ); // On affiche les colonnes pour la première fois uniquement
/* (3) On ajoute les contacts à la liste */
$output['common_']['contacts'] .= self::parseCSV($subjectData['contacts'], $dict['contacts'], strlen($output['common_']['contacts']) == 0 ); // On affiche les colonnes pour la première fois uniquement
}
// On ferme l'instance de la base de données
$db->close();
}
/* [4] On construit les fichiers de chaque sujet DE FORMULAIRE
=========================================================*/
if( $survey ){ // Si @survey vaut TRUE
// On ouvre une instance de la base de données
$db = new lightdb('survey_db', __ROOT__.'/src/dynamic/');
// Si on doit prendre tous les sujets, on les récupère
if( $all )
$subjects = array_keys( $db->index() );
// Pour chaque sujet
foreach($subjects as $s=>$subjectId){
/* (1) On récupère les données du sujet en cours */
$subjectData = $db->fetch($subjectId);
// Si on ne trouve rien, on passe au suivant
if( $subjectData === false )
continue;
/* (2) On complète les relations */
// {1} On retire les valeurs ou le type = 0 //
$formattedRelations = array();
foreach($subjectData['relations'] as $i=>$relation)
if( $relation['type'] != 0 ) // On retire les relations ego/alter
array_push($formattedRelations, array(
'source' => $relation['idA'],
'target' => $relation['idB'],
'weight' => ($relation['idA']==$subjectId) ? .1 : 1 // plus de poids aux relations alter/alter
));
// {2} On ajoute au contenu //
$output['common_']['relations'] .= self::parseCSV($formattedRelations, array(), strlen($output['common_']['relations']) == 0 ); // On affiche les colonnes pour la première fois uniquement
/* (3) On ajoute les contacts à la liste */
$output['common_']['contacts'] .= self::parseCSV($subjectData['contacts'], $dict['contacts'], strlen($output['common_']['contacts']) == 0 ); // On affiche les colonnes pour la première fois uniquement
}
// On ferme l'instance de la base de données
$db->close();
}
/* [5] On ajoute le dictionnaire
=========================================================*/
$output['common_']['dict'] .= "\"sheet\";\"field\";\"key\";\"value\"\r\n";
foreach($dict as $ds=>$dataset)
foreach($dataset as $f=>$field)
foreach($field as $key=>$value)
$output['common_']['dict'] .= "\"$ds\";\"$f\";\"$key\";\"$value\"\r\n";
/* [6] Création de l'archive
=========================================================*/
$zip = new \ZipArchive();
$fname = '/tmp/'.time().'.zip';
$zip->open($fname, \ZipArchive::CREATE);
foreach($output as $folder=>$files){
foreach($files as $file=>$content)
if( strlen($content) > 0 )
$zip->addFromString($folder.$file.'.csv', $content);
}
$zip->close();
/* [5] On lance le téléchargement
=========================================================*/
return array(
'ModuleError' => ManagerError::Success,
'headers' => array(
'Content-Type' => 'application/zip; charset=utf-8',
'Content-Disposition' => 'attachment; filename=graphics'.date('_d_m_Y_', time()).'.zip',
'Pragma' => 'no-cache',
'Expires' => '0'
),
'body' => file_get_contents($fname)
);
}
}

View File

@ -164,7 +164,8 @@
=========================================================*/
$funiq = fopen( __ROOT__.'/src/dynamic/uniqid', 'r+' );
flock($funiq, LOCK_EX); // On verrouille le fichier
$uniqid = fgets( $funiq );
$uniqid = trim( fgets( $funiq ) );
if( !is_numeric($uniqid) )
$uniqid = 0;

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"1":{"line":0,"hash":"8c61ad184a90f6fb8cad263c70f70a261cd79be1"},"284":{"line":1,"hash":"7612b6ef7aa53df76ac92789dcf48f9222435091"}}
[]

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"1":{"line":0,"hash":"af7f39edaef1c7c03ab4a2368ee37f62a8ba09e8"},"377":{"line":1,"hash":"2c10278d180745a62531c4f9d53471a08a5447b1"},"376":{"line":2,"hash":"91f713507ac91faed6eb20276ecf8bd091881b60"}}
{"1":{"line":0,"hash":"10a358a685d0fbdff78068d891fdd08f06011be4"},"2":{"line":1,"hash":"dad8b6bc0c54a329075ae2a5f55e188dc1fd13fa"}}

View File

@ -1,5 +1,8 @@
{"subject":{"id":1,"name":"Jean-Michel","creation":1464337747}}
{"subject":{"id":284,"name":"Julien","creation":1464343075}}
{"subject":{"id":375,"name":"Sujet 1","creation":1464595899}}
{"subject":{"id":376,"name":"Sujet 2","creation":1464595901}}
{"subject":{"id":377,"name":"Sujet 3","creation":1464595902}}
{"subject":{"id":1,"name":"Caroline","creation":1464788890}}
{"subject":{"id":2,"name":"Cl\u00e9ment","creation":1464788893}}
{"subject":{"id":3,"name":"Coralie","creation":1464788897}}
{"subject":{"id":4,"name":"Laurent","creation":1464788899}}
{"subject":{"id":5,"name":"Emilie","creation":1464788903}}
{"subject":{"id":6,"name":"C\u00e9line","creation":1464788905}}
{"subject":{"id":7,"name":"Pierre","creation":1464788908}}
{"subject":{"id":8,"name":"Boris","creation":1464788910}}

View File

@ -1 +1 @@
{"1":{"line":0,"hash":"b7ec0c5b00bda3ab9b6924938a247bbfe8b46fa2"},"284":{"line":1,"hash":"e8781daaaf4e4cd40c0525977a92428524b0f302"},"375":{"line":2,"hash":"0346572ed4a146e1fa6be96e13170f5597c0a116"},"376":{"line":3,"hash":"cf9745f08f10f39505a6ed36506b6b38e4a609ef"},"377":{"line":4,"hash":"5d9f26a6b15bc6461db2965cbc4147d3fc4d89cd"}}
{"1":{"line":0,"hash":"9798c2b945e222c48bff804d9819c0d98e88d257"},"2":{"line":1,"hash":"8192621a7f0013e137e63edffdadea6ab7f69565"},"3":{"line":2,"hash":"a7b9d5cf6cfb41c0b100d91d053032bdad3a80da"},"4":{"line":3,"hash":"ef73002b8217c3c3b87a62b8ae4c69b3994c114f"},"5":{"line":4,"hash":"5cf77d4e920f1b418bf78484bc1766fb84baabb9"},"6":{"line":5,"hash":"356e841ad04cb0f553fe47eab88bf550d6853c60"},"7":{"line":6,"hash":"7d070210aed5df40ee7e2ca4b54f29c672d32a89"},"8":{"line":7,"hash":"9e4936de2cf03d46135bbac3589a1e3ce68fb406"}}

View File

@ -1 +1 @@
3570
349

File diff suppressed because it is too large Load Diff

View File

@ -25,21 +25,57 @@ $pers = $request->answer();
<h4>Exporter les données au format .csv</h4>
<strong>Choix des sujets</strong>
<br><input type='radio' name='subject-group' data-name='group' id='group_0' value='all' ><label for='group_0'>Tous les sujets</label>
<br><input type='radio' name='subject-group' data-name='group' id='group_1' value='sel' checked><label for='group_1'>Les sujets selectionnés uniquement</label>
<br><input type='radio' name='export_subject-group' data-name='group' id='export_group_0' value='all' ><label for='export_group_0'>Tous les sujets</label>
<br><input type='radio' name='export_subject-group' data-name='group' id='export_group_1' value='sel' checked><label for='export_group_1'>Les sujets selectionnés uniquement</label>
<br><br>
<article style='display: inline;' id='subject-list'>
<article style='display: inline;' id='export_subject-list'>
<input type='number' data-name='subjects' placeholder='Sujet' style='width: 5em; display: block;'>
</article>
<input type='button' id='add-subject' class='primary' value='+' style='padding:.2em .6em; margin: .5em 0'>
<input type='button' id='export_add-subject' class='primary' value='+' style='padding:.2em .6em; margin: .5em 0'>
<br><br><br><strong>Types de données</strong>
<br><br><input type='checkbox' data-name='phone' value='1' id='phone_0' ><label for='phone_0'>Données cellulaires</label>
<br><br><input type='checkbox' data-name='facebook' value='1' id='facebook_0'><label for='facebook_0'>Données Facebook</label>
<br><br><input type='checkbox' data-name='restic' value='1' id='restic_0' ><label for='restic_0'>Données <i>Lab Surveys</i></label>
<br><br><input type='checkbox' data-name='phone' value='1' id='export_phone_0' ><label for='export_phone_0'>Données cellulaires</label>
<br><br><input type='checkbox' data-name='facebook' value='1' id='export_facebook_0'><label for='export_facebook_0'>Données Facebook</label>
<br><br><input type='checkbox' data-name='restic' value='1' id='export_restic_0' ><label for='export_restic_0'>Données <i>Lab Surveys</i></label>
<br><br><input type='submit' class='primary hover' style='padding:.5em 2em;' value='Télécharger' id='export-all'>
<br><br><input type='submit' class='primary hover' style='padding:.5em 2em;' value='Télécharger' id='export_export-all'>
</section>
</section>
<section data-sublink='charts'>
<section id='charts-form'>
<h4>Exporter les données au format .csv pour un logiciel de visualisation de réseau</h4>
<strong>Choix des sujets</strong>
<br><input type='radio' name='charts_subject-group' data-name='group' id='charts_group_0' value='all' ><label for='charts_group_0'>Tous les sujets</label>
<br><input type='radio' name='charts_subject-group' data-name='group' id='charts_group_1' value='sel' checked><label for='charts_group_1'>Les sujets selectionnés uniquement</label>
<br><br>
<article style='display: inline;' id='charts_subject-list'>
<input type='number' data-name='subjects' placeholder='Sujet' style='width: 5em; display: block;'>
</article>
<input type='button' id='charts_add-subject' class='primary' value='+' style='padding:.2em .6em; margin: .5em 0'>
<br><br><br><strong>Types de données</strong>
<br><br><input type='checkbox' data-name='phone' value='1' id='charts_phone_0' ><label for='charts_phone_0'>Données cellulaires</label>
<br><br><input type='checkbox' data-name='facebook' value='1' id='charts_facebook_0'><label for='charts_facebook_0'>Données Facebook</label>
<br><br><input type='checkbox' data-name='restic' value='1' id='charts_restic_0' ><label for='charts_restic_0'>Données <i>Lab Surveys</i></label>
<br><br><input type='submit' class='primary hover' style='padding:.5em 2em;' value='Télécharger' id='charts_export-all'>
</section>
</section>

8
view/js/data-min.js vendored
View File

@ -1,3 +1,5 @@
var subjectList=$("#subject-list"),subjectAdd=$("#add-subject"),exportDeflater=new FormDeflater(document.getElementById("export-form"),["input"],["data-name"]);subjectAdd.addEventListener("click",function(b){b=document.createElement("input");b.type="number";b.dataset.name="subjects";b.placeholder="Sujet";b.style="width: 5em; display: block;";subjectList.appendChild(b)},!1);
$("#export-form #export-all").addEventListener("click",function(b){var a=(new FormDeflater(subjectList,["input"],["data-name"])).deflate().subjects;b=[];a instanceof Array||(a=[a]);for(var c=0;c<a.length;c++)0<a[c].length&&!isNaN(a[c])&&b.push(a[c]);a=exportDeflater.deflate();c={path:"download/multiple",phone:"1"==a.phone,facebook:"1"==a.facebook,survey:"1"==a.survey,all:"all"==a.group};"all"!=a.group&&0<b.length&&(c.subjects=b);api.send(c,function(a){if(0!=a.ModuleError)return!1;document.location=
a.link;Notification.success("OK","Lancement du t\u00e9l\u00e9chargement..")})},!1);
var exportSubjectList=$("#export_subject-list"),exportSubjectAdd=$("#export_add-subject"),exportDeflater=new FormDeflater(document.getElementById("export-form"),["input"],["data-name"]);exportSubjectAdd.addEventListener("click",function(b){b=document.createElement("input");b.type="number";b.dataset.name="subjects";b.placeholder="Sujet";b.style="width: 5em; display: block;";exportSubjectList.appendChild(b)},!1);
$("#export-form #export_export-all").addEventListener("click",function(b){var a=(new FormDeflater(exportSubjectList,["input"],["data-name"])).deflate().subjects;b=[];a instanceof Array||(a=[a]);for(var c=0;c<a.length;c++)0<a[c].length&&!isNaN(a[c])&&b.push(a[c]);a=exportDeflater.deflate();c={path:"download/multiple",phone:"1"==a.phone,facebook:"1"==a.facebook,survey:"1"==a.survey,all:"all"==a.group};"all"!=a.group&&0<b.length&&(c.subjects=b);api.send(c,function(a){if(0!=a.ModuleError)return!1;document.location=
a.link;Notification.success("OK","Lancement du t\u00e9l\u00e9chargement..")})},!1);var chartsSubjectList=$("#charts_subject-list"),chartsSubjectAdd=$("#charts_add-subject"),chartsDeflater=new FormDeflater(document.getElementById("charts-form"),["input"],["data-name"]);chartsSubjectAdd.addEventListener("click",function(b){b=document.createElement("input");b.type="number";b.dataset.name="subjects";b.placeholder="Sujet";b.style="width: 5em; display: block;";chartsSubjectList.appendChild(b)},!1);
$("#charts-form #charts_export-all").addEventListener("click",function(b){var a=(new FormDeflater(chartsSubjectList,["input"],["data-name"])).deflate().subjects;b=[];a instanceof Array||(a=[a]);for(var c=0;c<a.length;c++)0<a[c].length&&!isNaN(a[c])&&b.push(a[c]);a=chartsDeflater.deflate();c={path:"download/chart",phone:"1"==a.phone,facebook:"1"==a.facebook,survey:"1"==a.survey,all:"all"==a.group};"all"!=a.group&&0<b.length&&(c.subjects=b);api.send(c,function(a){console.warn(a);if(0!=a.ModuleError)return!1;
document.location=a.link;Notification.success("OK","Lancement du t\u00e9l\u00e9chargement..")})},!1);

View File

@ -2,8 +2,8 @@
/* [0] Gestion de l'export
=========================================================*/
/* (0) Initialisation des variables */
var subjectList = $('#subject-list');
var subjectAdd = $('#add-subject');
var exportSubjectList = $('#export_subject-list');
var exportSubjectAdd = $('#export_add-subject');
var exportDeflater = new FormDeflater(
document.getElementById('export-form'),
@ -12,7 +12,7 @@ var exportDeflater = new FormDeflater(
);
/* (1) Gestion de l'ajout dynamique des sujets */
subjectAdd.addEventListener('click', function(e){
exportSubjectAdd.addEventListener('click', function(e){
// On ajoute un input de sujet à la liste
var newElement = document.createElement('input');
@ -21,17 +21,17 @@ subjectAdd.addEventListener('click', function(e){
newElement.placeholder = 'Sujet';
newElement.style = 'width: 5em; display: block;';
subjectList.appendChild(newElement);
exportSubjectList.appendChild(newElement);
}, false);
/* (2) Export des données */
$('#export-form #export-all').addEventListener('click', function(e){
$('#export-form #export_export-all').addEventListener('click', function(e){
// {1} On récupère la liste des ids des sujets //
var subjectDeflater = new FormDeflater(subjectList, ['input'], ['data-name']);
var subjectDeflater = new FormDeflater(exportSubjectList, ['input'], ['data-name']);
var subjectDeflated = subjectDeflater.deflate()['subjects'];
// {2} On supprime les valeurs incorrectes //
@ -76,3 +76,92 @@ $('#export-form #export-all').addEventListener('click', function(e){
});
}, false);
/* [0] Gestion de l'export
=========================================================*/
/* (0) Initialisation des variables */
var chartsSubjectList = $('#charts_subject-list');
var chartsSubjectAdd = $('#charts_add-subject');
var chartsDeflater = new FormDeflater(
document.getElementById('charts-form'),
['input'], // éléments à éviter (tagName)
['data-name'] // Attributs par ordre de priorité
);
/* (1) Gestion de l'ajout dynamique des sujets */
chartsSubjectAdd.addEventListener('click', function(e){
// On ajoute un input de sujet à la liste
var newElement = document.createElement('input');
newElement.type = 'number';
newElement.dataset.name = 'subjects';
newElement.placeholder = 'Sujet';
newElement.style = 'width: 5em; display: block;';
chartsSubjectList.appendChild(newElement);
}, false);
/* (2) Export des données */
$('#charts-form #charts_export-all').addEventListener('click', function(e){
// {1} On récupère la liste des ids des sujets //
var subjectDeflater = new FormDeflater(chartsSubjectList, ['input'], ['data-name']);
var subjectDeflated = subjectDeflater.deflate()['subjects'];
// {2} On supprime les valeurs incorrectes //
var subjects = [];
// Si on a q'une valeur, on la met en tableau
if( !(subjectDeflated instanceof Array) )
subjectDeflated = [subjectDeflated];
for( var i = 0 ; i < subjectDeflated.length ; i++ )
if( subjectDeflated[i].length > 0 && !isNaN(subjectDeflated[i]) )
subjects.push( subjectDeflated[i] );
// {3} On récupère les types de données que l'on veut //
var deflated = chartsDeflater.deflate();
/* (4) On construit la requête */
var request = {
path: 'download/chart',
phone: deflated.phone == '1', // VRAI si on veut les données téléphoniques
facebook: deflated.facebook == '1', // VRAI si on veut les données facebook
survey: deflated.survey == '1', // VRAI si on veut les données ResTIC
all: deflated.group == 'all' // VRAI si on veut TOUS les sujets
};
// On ajoute les sujets s'il y en a
if( deflated.group != 'all' && subjects.length > 0 )
request.subjects = subjects;
/* (5) On lance la requête */
api.send(request, function(res){
// Si erreur
if( res.ModuleError != 0 )
return false;
// Sinon on lance le téléchargement
document.location = res.link;
Notification.success('OK', 'Lancement du téléchargement..');
});
}, false);