CérénIT

Le blog tech de Nicolas Steinmetz (Time Series, IoT, Web, Ops, Data)

Ma comptabilité, une série temporelle comme les autres - partie 3 - exécution distante

warp10timeseriescomptabilitéremote executionrexeclmaptemplate

Suite de notre épopée :

A l'issue du précédent billet, depuis le WarpStudio et en stockant les données dans la sandbox, nous avons manipuler les données pour :

  • refaire les précisions de juin à décembre 2020 à partir des données de 01/2017 à 05/2017
  • comparer ses prévisions avec les résultats réels
  • faire les prévisions pour 2021.

Cependant, cela n'est pas parfait :

  • La durée de vie des données dans la sandbox est limitée à 2 semaines
  • Il faut copier/coller ses requêtes dans le studio pour obtenir les graphiques
  • Mais cela permet d'évaluer les extensions commerciales déployées sur la sandbox mais que vous n'avez pas forcément sur votre instance Warp 10 (au moins pour le moment)

Nous allons voir aujourd'hui comment rappatrier les données générées dans sa propre instance Warp 10.

Génération des prévisions

Pour commencer du bon pied et être sur que tout le monde est au même niveau, nous allons regénérer les prévisions et chaque prévision sera alors stockée dans une sériée dédiée. Cela nous permet d'avoir notre jeu de données de départ. On doit pouvoir sauver directement nos données dans une base distante, mais pour simplifier le tutoriel, nous allons le faire en deux étapes.

'<read token>' 'readToken' STORE
'<write token>' 'writeToken' STORE

// Récupération des séries 2017 > 2020
[ $readToken 'expense' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2021-01-01T00:00:00Z' ] FETCH
0 GET
'exp' STORE

[ $readToken 'revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2021-01-01T00:00:00Z' ] FETCH
0 GET
'revenue' STORE

[ $readToken 'result' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2021-01-01T00:00:00Z' ] FETCH
0 GET
'result' STORE

// Prévision sur les 12 prochains mois avec AUTO
// On n'a pas besoin d'afficher les données
// Donc plus besoin de les stocker sous forme de variable avec utilisation de la fonction STORE
// Par contre, on veut perstister les données en base
// ce qui se fait avec UPDATE et l'utilsation d'un token en écriture
[ $result mapper.todouble 0 0 0 ] MAP
AUTO 12 FORECAST.ADDVALUES
"auto_result" RENAME
$writeToken UPDATE

[ $revenue mapper.todouble 0 0 0 ] MAP
AUTO 12 FORECAST.ADDVALUES
"auto_revenue" RENAME
$writeToken UPDATE

[ $exp mapper.todouble 0 0 0 ] MAP
AUTO 12 FORECAST.ADDVALUES
"auto_expense" RENAME
$writeToken UPDATE

# Prévisions avec SAUTO
[ $result mapper.todouble 0 0 0 ] MAP
12 SAUTO 12 FORECAST.ADDVALUES
"sauto_result" RENAME
$writeToken UPDATE

[ $revenue mapper.todouble 0 0 0 ] MAP
12 SAUTO 12 FORECAST.ADDVALUES
"sauto_revenue" RENAME
$writeToken UPDATE

[ $exp mapper.todouble 0 0 0 ] MAP
12 SAUTO 12 FORECAST.ADDVALUES
"sauto_expense" RENAME
$writeToken UPDATE

Si nous vouulons vérifier que vos données sont bien en base, il faut utiliser FETCH :

// Ex de FETCH avec la série auto_revenue
'<read token>' 'readToken' STORE
[ $readToken 'auto_revenue' { 'company' '=cerenit' } '2016-12-01T00:00:00Z' '2022-01-01T00:00:00Z' ] FETCH
0 GET

Attaquons maintenant la grande traversée des données vers mon instance Warp 10.

Activation de l'exécution à distance

Alors on pourrait simplement migrer les données à coup de "curl in/curl out" mais l'idée ici est plus d'illustrer les interactions possibles entre des instances Warp 10.

Pour exécuter du warpscrip sur une instance distante, il faut utiliser la fonction REXEC

Cette exécution distante est désactivée par défaut, il faut donc activer cette extension:

Dans /path/to/warp10/etc/conf.d/70--extensions.conf, nous avons :

// REXEC
#warpscript.extension.rexec = io.warp10.script.ext.rexec.RexecWarpScriptExtension
#warpscript.rexec.endpoint.patterns = .*
// REXEC connect timeout in ms (default = 0, no timeout)
#warpscript.rexec.timeout.connect = 3600000
// REXEC read timeout in ms (default = 0, no timeout)
#warpscript.rexec.timeout.read = 3600000

Décommentons ces lignes et relançons warp 10.

Pour valider que REXEC est bien activé, depuis le studio, nous pouvons faire un test simple:

'2 2 +' 'https://warp10.url:port/api/v0/exec' REXEC

La réponse est 4.

Attention, il faut choisir votre instance comme endpoint dans la liste déroulante du studio. Si vous êtes sur le endpoint de la sandbox, vous auez le message d'erreur suivant :

https://sandbox.senx.io/api/v0/exec
line #2: Exception at ''2%202%20+' 'https://warp10.url:port/api/v0/exec' =>REXEC<=' in section [TOP] (REXEC encountered a forbidden URL 'http://warp10.url:port/api/v0/exec')

En effet, depuis la Sandbox, il n'est pas possible d'accéder à n'importe quelle machine par mesure de sécurité.

Requêtage simple à distance

Dans le studio, à partir de maintenant, il doit être configuré pour utliiser votre instance Warp 10 comme endpoint.

Le test simple étant fonctionnel, passons à un test un peu plus compliqué, à savoir recupérer nos données hébergées depuis la sandbox en lecture pour le moment.

// tokens pour la sandbox
'<sandboxReadToken>' 'sandboxReadToken' STORE
'<sandboxWriteToken>' 'sandoxWriteToken' STORE

// Url de la sandbox
'https://sandbox.senx.io/api/v0/exec' 'url' STORE

// On introduit ici la notion de template - comme on va vouloir récupérer plusieurs séries avec les mêmes paramètres
// Autant automatiser un peu et s'appuyer sur une boucle ! :-)
// On crée donc un TEMPLATE pour la fonction FETCH qui va récupérer un token en écriture
// et un nom de classe permettant de récupérer nos GTS.
// Rappel le <' ... '> permet de faire des strings en multi-lignes
// On stocke le template sous la forme d'une variable fetchTpl.
<'
  {
    'token' '{{ remoteReadToken }}'
    'class' '{{ remoteClass }}'
    'labels' {}
    'start' '2016-12-01T00:00:00Z'
    'end' '2022-01-31T00:00:00Z'
  } FETCH
'>
'fetchTpl' STORE

// Avec la fonction TEMPLATE, on remplace les clés par leurs valeurs en fournissant le template
// et un dictionnaire à la fonction.
$fetchTpl
{ 'remoteReadToken' $sandboxReadToken 'remoteClass' 'revenue' } TEMPLATE

// Execution de la requête distante avec REXECZ
// La différence avec REXEC est qu'une compression est appliquée sur la réponse à la requête
$url REXECZ
// La liste de GTS issue de FETCH ne contient qu'une liste, on prend donc la première
0 GET
// Stockage sous la forme d'une variable
'revenueGTS' STORE
// Affichage de la série
$revenueGTS

Si vous allez dans l'onglet dataviz, vous pouvez constater que vos données issues de la sandbox mais qui ont transité via votre instance sont bien disponibles.

Transfert des données - 1 série

Si nous commençons par une seule série :

// tokens de l'instance
'<instanceReadToken>' 'instanceReadToken' STORE
'<instanceWriteToken>' 'instanceWriteToken' STORE

// tokens pour la sandbox
'<sandboxReadToken>' 'sandboxReadToken' STORE
'<sandboxWriteToken>' 'sandoxWriteToken' STORE

// Url de la sandbox
'https://sandbox.senx.io/api/v0/exec' 'url' STORE

// Template de code warpscript
<'
  {
    'token' '{{ remoteReadToken }}'
    'class' '{{ remoteClass }}'
    'labels' {}
    'start' '2016-12-01T00:00:00Z'
    'end' '2022-01-31T00:00:00Z'
  } FETCH
'>
'fetchTpl' STORE

// Substitution des variables
$fetchTpl
{ 'remoteReadToken' $sandboxReadToken 'remoteClass' 'revenue' } TEMPLATE
// Exécution de la requête
$url REXECZ
// La liste de GTS issue de FETCH ne contient qu'une liste, on prend donc la première
0 GET
// Il faut renommer "localement" la série avant de pouvoir la stocker dans l'instance
// Peut éviter de mauvaises manipulations que l'on pourrait regretter :-)
"revenue" RENAME
// Persistance des données
$instanceWriteToken UPDATE

Il y a quelques occurences de "revenue" en dur dans le code, il va falloir améliorer cela.

Transfert des données - plusieurs séries

Et maintenant, traitons nos 9 series d'un coup

// tokens de l'instance
'<instanceReadToken>' 'instanceReadToken' STORE
'<instanceWriteToken>' 'instanceWriteToken' STORE

// tokens pour la sandbox
'<sandboxReadToken>' 'sandboxReadToken' STORE
'<sandboxWriteToken>' 'sandoxWriteToken' STORE

// Url de la sandbox
'https://sandbox.senx.io/api/v0/exec' 'url' STORE

// Template de code warpscript
<'
  {
    'token' '{{ remoteReadToken }}'
    'class' '{{ remoteClass }}'
    'labels' {}
    'start' '2016-12-01T00:00:00Z'
    'end' '2022-01-31T00:00:00Z'
  } FETCH
'>
'fetchTpl' STORE

// Création d'une liste avec nos 9 séries
// C'est cette liste que nous allons passer ensuite dans une MACRO.
// Cette MACRO va être exécutée sur chaque élément de la liste via l'utilisation de la fonction LMAP
// https://www.warp10.io/doc/LMAP
[ 'revenue' 'exp' 'result' 'auto_revenue' 'auto_result' 'auto_expense' 'sauto_revenue' 'sauto_result' 'sauto_expense' ]
// Début de la MACRO
<%
  // On récupère la valeur de la liste que l'on stocke sous la forme d'une variable
  'remoteClass' STORE

  // Substitution des valeurs de template
  $fetchTpl
  { 'remoteReadToken' $sandboxReadToken 'remoteClass' $remoteClass } TEMPLATE

  // Exécution distante de la requête
  $url REXECZ
  // On récupère ici une liste de GTS - plutôt que d'en extraire la GTS comme précédemment
  // on va garder une liste de GTS à 1 élément, mais ce qui permet à nouveau d'utiliser la fonction LMAP
  // Sur chaque entrée de la liste, une seconde macro est appliquée
  // Le contenu de notre macro consiste à utliser la fonction RENAME
  // '+' RENAME, cela revient à renommer la GTS en prenant le même nom que celui qui est fourni
  // '+x' RENAME aurait ajouté un x au nom de la série
  // Il reste l'index de la liste à traiter - soit on le supprime avec DROP
  //<% DROP '+' RENAME %> LMAP
  // Soit on passe F comme 3ème argument à LMAP - cela permet d'ignorer cet index
  // <% '+' RENAME %> F LMAP
  // Prenons la seconde forme :
  <% '+' RENAME %> F LMAP
  // Toutes nos séries ont été correctement renommées !
  // On persiste la GTS dans la base locale
  $instanceWriteToken UPDATE
%>
// Fin de la MACRO
// Application de la fonction LMAP pour que notre macro soit exécutée sur chaque élément de la liste.
// Comme on ne veut que les valeurs de la liste et pas les index, on positionne aussi F
// comme 3ème argument à LMAP
F LMAP

Et voilà, nos données ont été récupérées de la Sandbox et stockées dans notre instance locale.

Bonus

Une version alternative - dans mes données, je peux tricher et ne filtrer que sur le label company avec pour valeur cerenit:

// tokens de l'instance
'<instanceReadToken>' 'instanceReadToken' STORE
'<instanceWriteToken>' 'instanceWriteToken' STORE

// tokens pour la sandbox
'<sandboxReadToken>' 'sandboxReadToken' STORE
'<sandboxWriteToken>' 'sandoxWriteToken' STORE

// Url de la sandbox
'https://sandbox.senx.io/api/v0/exec' 'url' STORE

// Warpscript template
<'
  {
    'token' '{{ remoteReadToken }}'
    'class' '~.*'
    'labels' { 'company' 'cerenit' }
    'start' '2016-12-01T00:00:00Z'
    'end' '2022-01-31T00:00:00Z'
  } FETCH
'>
'fetchTpl' STORE

// Substitution dans le template
$fetchTpl
{ 'remoteReadToken' $sandboxReadToken } TEMPLATE
// Execution de la requête
$url REXECZ
// Renommage des séries
<% '+' RENAME %> F LMAP
// Presistence des données
$instanceWriteToken UPDATE

Bravo si vous m'avez suivi jusqu'ici, nous avons pu voir l'utilisation de :

  • Comment permettre un requêtage à distance d'une instance Warp 10 avec les fonctions REXEC et REXECZ
  • Comment traiter dynamiquement notre liste de GTS avec LMAP et une MACRO
  • Comment faire un template warpscript et de la substitution de variables avec TEMPLATE

Nous verrons dans un prochain épisode :

  • Comment utiliser Discovery pour faire nos premiers dashboards
  • Pourquoi/comment utiliser des macros coté server (spoiler: pour éviter que vos tokens se retrouvent dans votre navigateur à l'exécution des dashboards)
1 / 1