Uploader en ajax un fichier avec barre de progression

Vous savez sans doute uploader via un formulaire une image vers un serveur, et aussi via ajax (c’est un peu plus compliqué). Mais il est possible d’uploader en Ajax progressivement par morceau (chunk en anglais).

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>BLOB upload</title>
</head>

<body>
    <form action="#">
        <input type="file" value="" id="file" name="file"><br>
        <input type="submit" id="uploadBtn" value="UPLOAD">
    </form>
</body>
<script>
...
</script>
</html>

Grâce à l’attribut name on peut faire référence au fichier. Dans la balise script on a le code suivant :

<script>
    const uploadBtn = document.querySelector('#uploadBtn')
    const url = 'http://localhost.test/blobupload.php'

    uploadBtn.addEventListener('click', function (e) {
        e.preventDefault()
        const file = document.querySelector('#file').files[0]
        uploadBlob(file)

    })


</script>

La fonction uploadBlob() va prendre le fichier qui est passé en argument, puis le décomposer dans une boucle en plusieurs chunk de taille fixe. Notre boucle se termine quand on a atteint la fin du fichier.

<script>
    const uploadBtn = document.querySelector('#uploadBtn')
    const url = 'http://localhost.test/blobupload.php'

    uploadBtn.addEventListener('click', function (e) {
        e.preventDefault()
        const file = document.querySelector('#file').files[0]
        uploadBlob(file)

    })
  
  function uploadBlob(file) {
        
        const chunkSize = 100_000;
      
        let start = 0

        for (let start = 0; start < file.size; start += chunkSize) {

            const chunk = file.slice(start, start + chunkSize);
            const fd = new FormData();
            fd.set('file', chunk,);


            fetch(url, {
                method: "POST",
                body: chunk
            })
                .then(response => alert('Blob Uploaded'))
                .catch(err => alert(err));
        }
    }
</script>

Note boucle for va saucissonner le fichier en portions égales (à l’exception du dernier tronçon) à chaque itération. Ensuite ce morceau sera uploadé.

Côté backend pour la réception des fichiers en PHP

<?php
//  blobupload.php
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
$result = file_get_contents('php://input');
$written = file_put_contents('cpu.png', $result, FILE_APPEND);
echo $written;

C’est plutôt simple côté backend, le code est synchrone. Il retourne la quantité de bytes écrits ( 1 byte = 1 octet)

Essai d’upload par paquet en javascript

Nous allons prendre un fichier qui fait plus de 100000 octets pour déclencher la découpe. Pour l’exemple j’ai une image de 811 Ko nommée cpu4.png. Cela fait combien de paquets (chunks) à envoyer? Simple division 811000 / 100000 = 9. Le dernier paquet fait moins de 100000 octets ou bytes.

Image que nous allons envoyer.

On ouvre les devtools, et effectivement j’ai 9 envois.

Si vous essyez d’ouvrir l’image cependant, vous ne pourrez pas la visualiser. En fait l’image uploadée est différente de l’image choisie, vous n’avez qu’à comparer leurs checksum sur un outil comme celui-ci.

Mais comment est ce possible? Regardez l’image ci-dessus, le fichier php renvoit la taille écrite en guise de réponse, et l’avant dernier paquet montre 29669 octets, alors que ce devait être le dernier paquet qui doit montrer ce nombre ! Bref les paquets sont envoyés dans le désordre !! Nous allons corriger cela simplement avec async/await, qui va rendre un peu de synchronisme dans la boucle for.

Async/await à la rescousse.

Nous allons modifier le code de la façon suivante : uploadBlob sera async et on « awaitera » fetch.

    async function uploadBlob(file) {
        //reader = new FileReader() // le file reader va lire à partir d'une position donnée
        const chunkSize = 100_000;
        // const filename = file.name;
        let start = 0
        let i = 0

        for (let start = 0; start < file.size; start += chunkSize) {
            console.log("morceau ", i, "uploadé")
            i++
            let end = start + chunkSize
            const chunk = file.slice(start, end);
            const fd = new FormData();
            fd.set('file', chunk,);


            await fetch(url, {
                method: "POST",
                body: chunk
            })
                .then(response => response.text())

        }
    }

Maintenant le dernier paquet est bien le plus petit. Tous les paquets sont envoyés dans l’ordre. Nous pouvons vérifier que l’image s’ouvre bien. Vérifiez les checksum de ces deux fichiers, ils sont pareil.

Il est à noter que dans le cas des envois en désordre, la taille du fichier reçu est variable !

Utiliser le webservice de Github pour extraire des informations

Récemment, pour un projet open source, j’ai voulu afficher un classement des contributeur en fonction du nombre de commits. J’ai tout de suite eu l’idée de passer par le webservice de Github (alors que je n’en ai jamais entendu parler).

Il est normal que Github ait un webservice, cela permet de faire des nitégration un peut partout (plugin git par exemple).

Comment mener sa réflexion du webservice à l’affichage des badges pour récompenser les contributeurs?

L’idée de base est de proposer un classement des contributeurs en fonction du nombre de pull requests. Pour avoir les pull request, il faut avoir les commits sur la branch principale (dans notre cas develop).

Il a fallut d’abord trouver un moyen via la documentation de lister les commits d’une branche. Deplus j’ai pris le parti d’utiliser un token pour avoir moins de restriction sur les appels.

Exemple d’appel sans avoir besoin de clé:

https://api.github.com/user/repos

Appel avec un clé

Il faut d’abord générer une clé, plus exactement un PAT (personal Access Token). Pour ce faire aller à Settings, puis dans la barre de gauche aller dans Developper…

Générer le PAT, sélectionner les droits de ce PAT, je n’ai besoin qu’en lecture, donc cocher comme dans l’image. Je vous propose dans un premier temps d’expérimenter avec Postman, pour ceux qui ne le connaissent pas encore, Postman permet de faire des requêtes HTTP sans navigateur, pratique pour tester des webservices.

Vous aurez les repository du propriétaqire de votre token. Maintenant listons les commits d’une branches, il a fallu explorer la documentation, ne la baclez pas, la documentation est riche.

// lister les branches
https://api.github.com/repos/refschool/Clone-Weetransfert/branches

// lister les commits de la branche main
https://api.github.com/repos/refschool/Clone-Weetransfert/branches/main

//  lister les commits de la branche par défaut qui est main
https://api.github.com/repos/refschool/Clone-Weetransfert/commits

//  lister les commits de la branche develop avec 100 résultats par page.
https://api.github.com/repos/refschool/Clone-Weetransfert/commits?per_page=100&sha=develop

Il faut essayer de se familiariser avec la pagination le jour où l’on a vraiment beaucoup de commit.

Lien vers la documentation:

https://docs.github.com/en/rest/commits/commits?apiVersion=2022-11-28

Mais ne peut on pas lister les pull request?

A l’évidence si ! voici la documentation :

https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests

vous devez être le propriétaire du repo pour avoir les pull requests du projet (normal)

Installer docker sur DigitalOcean Droplet avec Docker

Dans cet article on va voir comment déployer des container docker d’une application multicontainer dans un VPS à l’aide de Docker-compose. Sur DigitalOcean, la manipulation est plus facile que sur Azure ou AWS (le plus complexe)

Création du Droplet

Un droplet sur DigitalOcean (un prestataire Cloud abordable et pas trop compliqué à utiliser),

1/Créer un compte sur DigitalOcean

2/créer un droplet l’équivalent d’un VPS, choisir Ubuntu comme OS avec Docker préinstallé en vous aidant du Marketplace. Vous créerez par la même occasion un projet qui apparaitra dans la sidebar.

Ensuite vous choisissez le Datacenter le plus proche de chez vous, en France l’on choisira Amsterdam par exemple.

Dans la section « Choose an image« , cliquez sur l’onglet Marketplace, puis sur l’image Docker on Ubuntu, ici l’image représente une machine virtuelle bien sûr et pas une image Docker.

Ensuite choisissez les options les moins chères, dans Droplet Type, choisissez « Basic« , dans CPU options, choisissez « Regular« , et la formule au mois la moins chère.

Dans « Choose Authentication Method« , choisissez Password pour faire simple, et SSH Key pour faire pratique (pas de mémorisation de mot de passe et login nécessaire). Allez sur ce post pour générer et mettre votre clé sur le service. Enfin cliquez sur « Create Droplet ». Au bout d’un certain temps votre droplet sera créée avec Docker installé, cependant Docker-Compose ne sera pas encore installé. Il vous faudra faire via la console.

Si vous voulez installer à la main Docker, les instruction sont sur cette page :

https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-22-04

Cloner votre projet depuis Github

Pour vous connecter en console dans votre Droplet dans la sidebar « Projects« , cliquez sur votre projet

Installer docker-compose

Une fois dedans cliquez sur « Console« , cela ouvrira un shell. Installez docker-compose

sudo apt-get install docker-compose-plugin

ou tapez docker-compose, une erreur avec un message sur comment installer docker-compose s'affichera.

Vous êtes en connecté en root, allez à /home et cloner le projet. Le projet consiste en un front end en ReactJS, un backend en Node/Express et de MongoDB. Ce dernier est directement tiré du hub de Docker. Alors que le front et le back vont être transformé en image via leurs Dockerfiles respectifs.

Démarrage du docker-compose avec le fichier de prod

Dans la racine du projet où il y a le fichier docker-compose-prod.yml

docker-compose -f docker-compose-prod.yml up -d

Cette unique ligne va créer ou importer les images, démarrer les containers et in fine votre application.

Les problèmes et leurs solutions

Problème de CORS

Ceci est vraisemblablement dû à un problème de CORS (cross origin), si on ouvre les devtools, onglet Réseau, en inspectant la requête, vous aurez les indices. La requête est faite à http://localhost:3001/api alors que notre serveur possède une adresse IP (voir image)

En effet en phase de développement on a http://localhost:3001/api (voir le fichier src/services/api.js , si la variable d’environnement REACT_APP_API_URL est settée, elle surchargera localhost.

const baseUrl = process.env.REACT_APP_API_URL || "http://localhost:3001/api";

A la racine du projet, il y a un fichier docker-compose-prod.yml. Dans le Dockerfile du front il vous faudra remplacer dans le fichier Dockerfile après COPY

#Dockerfile
...
COPY . .
ENV REACT_APP_API_URL=http://adresse-ip-du-droplet:3001/api
...

Problème de droits de fichier

Vérifiez aussi que dans le dossier backend, les fichiers docker-entrypoint.sh et wait-for soient exécutables en faisant la commande chmod +x <fichier>.

Une fois que la commande docker-compose est faite, vous avez la main dans le shell, Inspectez les containers

docker ps

//Voir les logs d'un container
docker logs <id-container>

//Par exemple si le container du backend ne fonctionne pas correctement, lisez les logs.

//Pour arrêter
docker-compose down


//Pour relancer avec un build d'images

docker-compose -f docker-compose-prod.yml up -d --build

Utiliser Rollup pour fusionner vos fichiers javascript

Application d’origine sans bundle JS

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Bundler with Parcel</title>
</head>

<body>
    <form action="#">
        <label for="email">Email</label><br>
        <input type="text" value="" id="email"><br>
        <label for="password">Mot de passe</label><br>
        <input type="text" value="" id="password"><br>
        <input type="submit" id="btn" valu="Soumettre">
    </form>
</body>
<script src="./script1.js"></script>
<script src="./script2.js"></script>
<script src="./script3.js"></script>

</html>

Les différents scripts JS

//script1.js
let email = document.querySelector('#email')
let password = document.querySelector('#password')
let btn = document.querySelector('#btn')


//script2.js
email.addEventListener('keyup', (event) => {
    if (!isEmailValid(email.value)) {
        console.log('Email is invalid')
    } else {
        console.log('Email is valid')
    }
})

password.addEventListener('keyup', (event) => {

    console.log('Password length ', password.value.length)

})

// Vérifie si l'email est correct
const isEmailValid = (email) => {
    return email.toLowerCase()
        .match(
            /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
        );
}

//script3.js
btn.addEventListener('click', (event) => {
    event.preventDefault()
    if (isEmailValid(email.value) && password.value.length > 8) {
        alert("Form is valid")
    } else {
        alert("Form is invalid")

    }
})

Si votre page HTML fait appel à l’inclusion de beaucoup de fichier JS, vous aurez envie de fusionner tous ces fichiers en un seul pour le rendre plus compact. je vous propose d’utiliser Rollup, qui est simple comparé à Webpack. On va installer Rollup

npm install rollup @rollup/plugin-node-resolve --save-dev

Ensuite créer un fichier de configuration

import resolve from '@rollup/plugin-node-resolve';

export default {
  input: 'src/main.js',
  output: {
    file: 'dist/bundle.js',
    format: 'iife'
  },
  plugins: [resolve()]
};

Dans le fichier main.js vous importez les fichier Javascript

import './script1.js';
import './script2.js';
import './script3.js';

// etc

Si vous avez un message du genre

Ajoutez une propriété dans package.json

{
    "scripts": {
        "build": "rollup -c"
    },
    "devDependencies": {
        "@rollup/plugin-node-resolve": "^15.2.3",
        "rollup": "^4.18.1"
    },
    "type": "module"
}

Désormais dans le fichier HTML l’inclusion du Js ressemble à ça

//index.html
...
</body>
<script src="./dist/bundle.js"></script>
</html>

A quoi ressemble le bundle.js

(function () {
    'use strict';

    document.querySelector('#email');
    document.querySelector('#password');
    document.querySelector('#btn');

    email.addEventListener('keyup', (event) => {
        if (!isEmailValid$1(email.value)) {
            console.log('Email is invalid');
        } else {
            console.log('Email is valid');
        }
    });

    password.addEventListener('keyup', (event) => {

        console.log('Password length ', password.value.length);

    });

    // Vérifie si l'email est correct
    const isEmailValid$1 = (email) => {
        return email.toLowerCase()
            .match(
                /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
            );
    };

    btn.addEventListener('click', (event) => {
        event.preventDefault();
        if (isEmailValid(email.value) && password.value.length > 8) {
            alert("Form is valid");
        } else {
            alert("Form is invalid");

        }
    });

})();

Configurer un noeud sur Kaleido.io (Hyperledger)

tout d’abord il faut aller créer un Network

2/Ensuite créer un node, ensuite il faut aller dans le node en question, puis aller dans App, et faire Create an App

3/ Créer une nouvelle version pour pouvoir déployer ses chaincode

Un chaincode est l’équivalent d’un smartcontract, écrit en golang, il faut le compiler d’une certaine façon.

Comprendre les sockets avec Socket.io

Qu’est qu’un socket?

Un socket est une interface de communication entre deux noeuds dans un réseau informatique. Plus concrètement, entre deux ordinateurs, et pour les développeurs web entre un serveur et un client, aka votre ordinateur et plus précisément le navigateur.

Côté serveur

On va pour simplifier utiliser Nodejs, avec un script en local on va pouvoir en quelques ligne mettre en oeuvre un socket grâce à la librairie socket.io

Côté client

Grâce à une librairie socket.io-client, on va pouvoir se connecter à un socket.

Quelle est la différence entre un socket et une requête AJAX?

Dans le monde du HTTP, une communication entre client et serveur ne dure qu’un instant, entre deux communications il n’y a rien. Vous faites une requête AJAX, le client initie une demande (Request), le serveur réponse avec un Response, et puis c’est fini.

Dans le cas d’un socket, une fois que le client initie une demande de connexion, la communication s’établie et dure tant que l’on n’a pas arrêté, elle ne s’arrête pas toute seule. Le socket sert à maintenir une connexion persistente, avec un flot continu de données de part et d’autre.

Le socket répond au besoin de recevoir contnuellement des données, une application comme une courbe de bourse en temps réel a besoin d’un socket, cela permet une mise à jour temps réel du cours de bourse. Bien sû on pourrait faire toute les secondes une requête AJAX, mais c’est très consommateur de données, de par la nature du protocole HTTP. Socket ne fait pas partie de la technologie HTTP.

Démarrer un projet avec socket.io

Créez un nouveau répertoire de projet, ouvrez le avec VSCode, ouvrez un terminal sous VSCode.

Créer un fichier package.json comme ci-dessous:

{
  "name": "app-socket",
  "version": "0.1",
  "description": "mon socket.io app",
  "dependencies": {}
}

Installez Express

Express est un framework pour NodeJS en MVC.

npm install express@4

Créez le fichier index.js

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);

app.get('/', (req, res) => {
  res.send('<h1>Hello world</h1>');
});

server.listen(3000, () => {
  console.log('listening on *:3000');
});

vous pouvez lancer le serveur NodeJS avec la commande:

node index.js

ça va initialiser un serveur qui écoute sur le port 3000 (par défaut pour NodeJS). Si dans votre navigateur, vous ouvrez l’url http://localhost:3000. Gardez un oeil sur le terminal de VSCode également.

Ajout d’un fichier HTML

Nous allons améliorer notre code avec une page HTML à servir

<!DOCTYPE html>
<html>
  <head>
    <title>Socket.IO chat</title>
    <style>
      body { margin: 0; padding-bottom: 3rem; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }

      #form { background: rgba(0, 0, 0, 0.15); padding: 0.25rem; position: fixed; bottom: 0; left: 0; right: 0; display: flex; height: 3rem; box-sizing: border-box; backdrop-filter: blur(10px); }
      #input { border: none; padding: 0 1rem; flex-grow: 1; border-radius: 2rem; margin: 0.25rem; }
      #input:focus { outline: none; }
      #form > button { background: #333; border: none; padding: 0 1rem; margin: 0.25rem; border-radius: 3px; outline: none; color: #fff; }

      #messages { list-style-type: none; margin: 0; padding: 0; }
      #messages > li { padding: 0.5rem 1rem; }
      #messages > li:nth-child(odd) { background: #efefef; }
    </style>
  </head>
  <body>
    <ul id="messages"></ul>
    <form id="form" action="">
      <input id="input" autocomplete="off" /><button>Send</button>
    </form>
  </body>
</html>

Changez le code d’index.js

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

Relancez votre serveur avec node index.js

Note : Pour éviter de redémarrer à la main à chaque fois qu’il y a une mise à jour du code, je vous suggère d’installer nodemon, un démon NodeJS, qui va redémarrer le serveur automatiquement à chaque enregistrement de fichier.

npm install --save-dev nodemon  // installe en tant que dépendance de développement
// redémarrer le serveur
nodemon ./index.js localhost 3000

Intégration de socket.io

Maintenant on va allez voir les choses intéressantes, l’intégration de socket !

Il nous faut agir sur deux fronts, sur le serveur et sur le client. Il y a donc deux partie à la librairie socket.io. (nous sommes toujours en architecture client/serveur)

Socket.io côté serveur

npm install socket.io

et éditons le fichier index.js

const express = require('express');
const app = express();
const http = require('http');
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);

app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

// début de code de gestion du socket
io.on('connection', (socket) => {
  console.log('a user connected');
  socket.on('disconnect', () => {
    console.log('user disconnected');
  });
});

// fin de code de gestion du socket

server.listen(3000, () => {
  console.log('listening on *:3000');
});

Notez qu’on crée le serveur de socket en lui passant dans le constructeur le server NodeJS. « connection » est l’événement qui se déclenche lorsque la page dans le front end se charge pour la première fois et qu’un message donc est envoyé au serveur.

Socket.io côté client

Ajoutez le snippet dans le fichier HTML avant la fermeture de body

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

ça y est notre application web est équipée!

Maintenant ouvrez la page a port 3000, votre terminal nodeJS va afficher ‘a user connected’. Ce qui se passe c’est que lorsque la page est loadée, la librairie socket.io va envoyer un signal vers le serveur, qui intercepte le message. Si vous bne voyez rien, rafraichissez la page.

Construction de l’application de messagerie instantanée

Maintenant côté client, écrivons le code JS pour émettre les messages entrés au clavier, dans le formulaire:

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();

  var form = document.getElementById('form');
  var input = document.getElementById('input');

  form.addEventListener('submit', function(e) {
    e.preventDefault();
    if (input.value) {
      socket.emit('chat message', input.value);
      input.value = '';
    }
  });
</script>

et côté serveur on va afficher le message, ajoutez le snippet suivant :

io.on('connection', (socket) => {
...
  socket.on('chat message', (msg) => {
    console.log('message: ' + msg);
  });
});

Tapez les messages dans le formulaire, observez les logs côté serveur.

Le broadcasting côté serveur

Intéressons nous maintenant sur comment le serveur va émettre un message pour tous les client connectés, à la manière d’une station radio, d’où le terme ‘broadcasting’. Il faut utiliser la méthode emit() de l’objet io. Modifiez le code :

io.on('connection', (socket) => {
    console.log('a user connected');

    socket.on('disconnect', () => {
        console.log('user disconnected');
    });

    socket.on('chat message', (msg) => {
        console.log('message du client: ' + msg);
        io.emit('retour', { propriete1: 'valeur 1', propriete2: 'valeur2' })
    });
});

Configurer VSCode pour débugger

Il ya plusieurs façons de débugger NodeJS avec VSCode, le plus simple (mais peut être le plus ennuyant) est d’utiliser l’auto Attach, dès qu’un process NodeJS est démarré, VSCode se met en écoute. On va utiliser cette méthode. Pour plus d’information sur les autres méthodes allez sur cette page.

Utiliser les dev tools pour visualiser les communication via le socket

Vous pouvez voir les connexion socket comme vous pouviez le faire avec les requêtes Ajax. Dans les devtools, allez dans l’onglet Réseau, cliquez sur WS dans les filtres (à côté de XHR, Images etc.

Dans l’onglet Réponse, vous pouvez voir en fait les allées et venues des messages.

Programmation orientée événéments en Javascript

Ce que tous les développeurs web connaissent, ce sont les événements du DOM, les clics de souris, les survol, les chargements de page. Soit la page HTML suivante, un formulaire avec une div cachée qui se révèle dès que le formulaire change

<body>
    <style>
        #popup {
            border: black 1px solid;
            width: 50%;
            height: 10vh;
            border-radius: 5px;
            margin: 10px;
            display: none;
        }
    </style>
    <form action="#" id="formulaire">
        <label for="email">Email</label>
        <input type="text" id="email"><br>
        <label for="name">Name</label>
        <input type="text" id="name"><br>
        <input type="submit" value="Send">
    </form>
    <div id="popup">
        <h1>Form filled !</h1>
    </div>
</body>
    let email = document.querySelector('#email')
    let name = document.querySelector('#name')
    let formulaire = document.querySelector('#formulaire')
    let popup = document.querySelector('#popup')

    formulaire.addEventListener('change', (e) => {
        console.log('form has changed')
        popup.style.display = 'block'
    })

Mais il est aussi possible de lancer des événement depuis n’importe quel objet en Javascript, et ceci va nous permettre de coder notre application avec des événements, permettant une grande souplesse et découplage.

Par exemple, imaginez un formulaire avec login et mot de passe, di les deux champs sont validés, nous voulion faire apparaitre une popup.

    let email = document.querySelector('#email')
    let name = document.querySelector('#name')
    let formulaire = document.querySelector('#formulaire')
    let popup = document.querySelector('#popup')
    popup.addEventListener('formEvent', (e) => {
        popup.style.display = 'block'
    })
    //create the CustomEvent
    const formEvent = new CustomEvent('formEvent', { detail: "les détails" })



    formulaire.addEventListener('change', (e) => {
        console.log('form has changed')
        popup.dispatchEvent(formEvent)
        // popup.style.display = 'block'
    })

Il faut mettre bubble à true pour que l’événement se propage

  let email = document.querySelector('#email')
    let name = document.querySelector('#name')
    let formulaire = document.querySelector('#formulaire')
    let popup = document.querySelector('#popup')
    popup.addEventListener('formEvent', (e) => {
        popup.style.display = 'block'
    })
    //create the CustomEvent
    const formEvent = new CustomEvent('formEvent', { detail: "les détails",bubbles:true })



    formulaire.addEventListener('change', (e) => {
        console.log('form has changed')
        popup.dispatchEvent(formEvent)
        // popup.style.display = 'block'
    })

Déjouer les piège du mot clé this en Javascript

Le mot clé this en javascript sert dans 3 contextes différents, et son comportement diffère selon les contexte.

le mot clé this dans une classe

C’est sans doute le plus facile à appréhender, car c’est comme dans les autres langages de programmation comme Java ou PHP.

class Rectangle {
  constructor(hauteur, largeur) {
    this.hauteur = hauteur;
    this.largeur = largeur;
  }
}

Le mot clé this dans une fonction

function test() {
    console.log('this in a function', this);
}

const test2 = () => {
    console.log('this in arrow function', this);

}
test2() // objet window
test() // objet Window

Comme on est dnas l’espace global (Window) le mot clé this représente Window. En effet les deux fonction sont définie dans l’espace de nom global et non dans un objet.

Le mot clé this dans l’espace de nom global

dans une fonction, la valeur de this dépend de comment la fonction est appelée. Considérez this comme un paramètres caché de la fonction. this est l’objet dans lequel la fonction est appelée.





Le cas des fonctions flèches

function test() {
    console.log('this in a function', this);
}

const test2 = () => {
    console.log('this in arrow function', this);

}
// test2() // undefined
// test() // objet Window
const obj1 = { name: "obj1" };
const obj2 = { name: "obj2" };
obj1.test = test
obj2.test2 = test2
console.log(obj1.test()) // this est l'objet obj1
console.log('arrow ', obj2.test2()) // this est l'objet Window

Dans les fonctions flèche, le this représente toujours l’objet global Window, alors que dans les fonctions classiques this représente l’objet dans lequel la fonction est définie.

Le cas du strict-mode

"use strict";

function show() {
    console.log(this);
}

show(); // undefined

This dans un event listener

Ici c’est plus courant, vous avez déjà vu avec Jquery le $this, en Javascript pur c’est pareil avec this

<span id="btn">Click Me</span>
<script>
const btn = document.querySelector("#btn")
btn.addEventListener('click',function(event){

console.log(this)

}

</script>

Modifier la valeur de this avec bind,call et apply

const person1 = {
    name: 'Paul',
    surname: 'Auchan',
    sayName: function () {
        return this.name + " " + this.surname;
    }
}

const person2 = {
    name: 'Justin',
    surname: 'News'
}

console.log(person1.sayName.call(person2)); // le this sera le this de person2
//appel avec arguments

const person2 = {
    name: 'Justin',
    surname: 'News'
}

const person3 = {
    name: 'Paul',
    surname: 'Auchan',
    sayName: function (ville) {
        return this.name + " " + this.surname + ', ' + ville;
    }
}
console.log(person3.sayName.call(person2, "Paris")); // le this sera le this de person2

Cas de bind

Autant call et apply se ressemblent, bind n’exécute pas la fonction , il faut l’invoquer manuellement. bind ne fait que binder une variable

//bind
const person4 = {
    name: 'Jules',
    surname: 'Sanchez',
    sayName: function() {
        return this.name + " " + this.surname
    }
}

const person5 = {
    name: 'Rudy',
    surname: 'Hess'
}

const sayPerson2Name = person1.sayName.bind(person2)

console.log(sayPerson2Name())

Tutoriel backtesting avec Tradingview

Dans ce tutoriel nous allons voir comment simplement backtester une strétégie avec Tradingview, cet outil existe en gratuit et fait dans la verison gratuites énormément de chose.

On va écrire le script en Pinescript, et on va exécuter le backtest.

Ceci n’est pas un conseil financier.

strategy("Moving average Cross")

// Pinescript c'est L4G (langage de 4ème génération)
ema20 = ema(close,20)
ema50 = ema(close,50)


// on définit quand est ce qu'on long ou short
long = ema20 > ema50
short = ema20 < ema50

//ouverture du trade
strategy.entry("long", strategy.long,1.0,when=long)
strategy.entry("short", strategy.short,1.0,when=short)

// fermeture du trade
strategy.close("long",when=short)
strategy.close("short",when=long)

Cliquez sur « Update on chart »

Un fois que vous avez fait le script il faut clicker sur « Strategy tester », à côté de « Pine Editor », dans le sous menu, Overview vous pouvez voir la courbe de PNL

lien vers la documentation de Pinescript

définir une date de début et de fin

//@version=4
strategy("Moving average Cross")
ema20 = ema(close,20)
ema50 = ema(close,50)
long = ema20 > ema50
short = ema20 < ema50

start = timestamp(2021,04,01,0,0)
end = timestamp(2024,04,01,0,0)

if time >= start and time <= end
    strategy.entry("long", strategy.long,1.0,when=long)
    strategy.entry("short", strategy.short,1.0,when=short)
strategy.close("long",when=short)
strategy.close("short",when=long)

il faut mettre @version=4 sinon la condition if ne marche pas

Ensuite cliquez sur « Update on chart » pour que ça marche

Affichage des deux moyennes mobiles

//@version=4
strategy("Moving average Cross")
ema20 = ema(close,20)
ema50 = ema(close,50)
long = ema20 > ema50
short = ema20 < ema50

plot(ema50, title="ma50", color=#ffc1cc, linewidth=3)
plot(ema20, title="ma20", color=#00ffaa, linewidth=2)

start = timestamp(2021,04,01,0,0)
end = timestamp(2024,04,01,0,0)

if time >= start and time <= end
    strategy.entry("long", strategy.long,1.0,when=long)
    strategy.entry("short", strategy.short,1.0,when=short)
strategy.close("long",when=short)
strategy.close("short",when=long)

//Version avec Overlay sur la courbe des prix
il suffit de modifier la première ligne de code
strategy("Moving average Cross",overlay=true)

Ensuite cliquez sur « Update on chart » pour que mettre à jour le graphe, les deux moyennes mobiles sont dans le bas de l’écran séparés du graphique des prix. Ajoutez la propriété overlay=true pour superposer les deux graphique, auparavant supprimez le graphique du bas avec les deux moyennes mobiles.

Retarder l’entrée en position à la 10ème bougie en plus de la condition des moyennes mobiles

On fait ceci pour éviter que le croisement des moyennes mobiles ne soit que éphémères.

//@version=4
strategy("Moving average Cross",overlay=true)
ema20 = ema(close,20)
ema50 = ema(close,50)
long = ema20 > ema50
short = ema20 < ema50

//on attend la consolidation pour passer à l'action pour éviter de se faire stopper
longcondition = long and long[10] and not long[11]
shortcondition = short and short[10] and not short[11]

plot(ema50, title="ma50", color=#ffc1cc, linewidth=3)
plot(ema20, title="ma20", color=#00ffaa, linewidth=2)

start = timestamp(2021,04,01,0,0)
end = timestamp(2024,04,01,0,0)

if time >= start and time <= end
    strategy.entry("long", strategy.long,1.0,when=longcondition)
    strategy.entry("short", strategy.short,1.0,when=shortcondition)
strategy.close("long",when=shortcondition)
strategy.close("short",when=longcondition)

On a pris moins de trade mais le PNL est moins intéressant, mais vous voyez ici l’idée, on joue sur les potard afin d’améliorer le PNL. Une fois que vous avez des résultats intéressants, vous pouvez appliquer pour le futur dans votre stratégie de quant.

Après le réglage

Avertissement sur les limites du backtesting

Le backtesting n’est pas trivial, et ce qui est donnée par Tradingview peut ne pas être juste dans la réalité. Regardez cette vidéo

Le quant marche mieux dans le low time frame intraday avec des bougies d’un quart d’heure. je ne suis pas spécialiste et j’ai fait cet article à des fin didactique, je n’ai pas fait de robot de trading.

Simuler le clic d’une souris avec Javascript

Non je ne parle pas de l’événement « click » ou « onclick », mais carrément de créer un événement souris. En javascript nous disposons de MouseEvent, qui ne se limite pas au seul clic gauche ou droit, mais tient compte aussi des déplacements de la souris.

var clickEvent = new MouseEvent('click', {
    bubbles: true,
    cancellable: true,
    clientX: 32,
    clientY: 32,
})

Voici le la documentation officielle de MouseEvent. Cependant je dois aussi vous dire qu’il y a un autre événement appelé PointerEvent. Cet événement est plus générique, car il peut gérer les événement touch, multi contact, alors que MouseEvent ne peut pas. Mais dans cet article nous allons nous concentrer sur MouseEvent.

Description de MouseEvent

Les 3 coordonnées de la souris dans une page web

Le référentiel d’un élément HTML

Le référentiel de la fenêtre

Le référentiel du document

Mise en apllication de MouseEvent

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>MouseEvent</title>
    <script src="MouseEvent.js" defer></script>
    <link rel="stylesheet" href="style.css">
</head>

<body>

    <div class="container">
        <div class="header"></div>
        <div class="main">
            <h1>Titre</h1>
            <div class="card">
                <p>Ceci est un exemple pour illustrer MouseEvent</p>
            </div>
        </div>
    </div>
</body>


</html>

function simulateClick() {
    let titre = document.querySelector('h1')

    let clickEvent = new MouseEvent('click', {
        bubbles: true,
        cancellable: true,
        /*    offsetX: 20,
            offsetY: 20,*/
    })


    console.log('dispatch')
    titre.dispatchEvent(clickEvent)
    console.log('clicked')

}

let h2 = document.querySelector('h1').addEventListener('click', function (e) {
    alert('toto')
})

setTimeout(simulateClick, 300)

La fonction simulateClick() va sélectionner l’élément H1 et la mettre dans la variable titre, construit un objet Event (MouseEvent pour être plus précis, MouseEvent héritant de Event), ensuite et c’est là qu’il faut être attentif, c’est titre qui va émettre (dispatch) l’événement clickEvent.

Dans le programme principal, h2 qui représente le même élément que titre reçoit une simulation de click et donc une alert() va faire apparaitre une popup. au bout de 300 millisecondes.

Liens en rapport avec l’article:

/////////////////////// DOM onunload ////////////////////
https://stackoverflow.com/questions/11177233/javascript-event-that-runs-before-page-changes
https://stackoverflow.com/questions/446892/how-to-find-event-listeners-on-a-dom-node-in-javascript-or-in-debugging
https://stackoverflow.com/questions/4386300/javascript-dom-how-to-remove-all-event-listeners-of-a-dom-object/64484951#64484951

Comprendre les modules en javascript

Qu’est ce qu’un module en Javascript?

Un module est un fichier tout simplement rien de plus. On parle de module en javascript moderne, en effet ce concept n’existait pas avant ES6. Les modules existent en NodeJs également. Mais dans cet article je ne vais parler que des modules côté client.

Avant l’existence des modules, on ne pouvait pas importer un fichier Javascript dans un autre fichier Javascript comme on peut le faire en PHP.

Ici on ne va parler que des modules front end, dont la syntaxe est différente des modules dans NodeJS, dans ce dernier il existe au moins deux façon de faire, avec require() (commonJs) et import (AMD).

Comment on faisait avant pour importer un script Javascript?

#index.html
<html>
...
<body>
..
<script src="index.js"></script>
<script src="helper.js"></script>
...

On se servait de la balise script our importer les fichiers, index.js ne pouvait pas importer helper.js. La conséquence est que dans un fichier HTML on pouvait avoir facilement plus d’une dizaine d’import de fichier javascript. Cela pouvait avoir un impoact sur la rapidité du site.

Et vinrent les modules Javascript

Dès qu’il y a un import de modules, on observe au minimum deux fichiers. Soient deux fichiers, un principal.js, et un helper.js

//index.html
<script type="module">
import {sayHi} from './helper.js';

sayHi('John');
</script>
// helper.js
export function sayHi(name){
     console.log('Hello ' +  name)
}

On peut importer une fonction à condition qu’elle ait été exportée depuis le fichier helper.js. Dans la balise script on doit ajouter l’attribut type= »module » pour que cela marche.

Les caractéristiques de modules

Les modules utilisent le mode strict

Dans un code Javascript vous utilisez le mode strict pour forcer une plus grande rigueur dans la programmation

Par exemple dans un fichier HTML

<script>
a = 5  // autorisé pas d'erreur dans la console
</script>

Par contre

<script>
"use strict"
a = 5  // ReferenceError: a is not defined
</script>

de même dans un module
<script type="module">
a = 5 // ReferenceError: a is not defined  bien qu'on n'ait pas eu à mettre use strict 
</script>

Le scope des variables

Chaque module a son scope pour ses variables (je parle des variable globales du module, pas les variable locales des fonctions), les modules ne communiquenet donc pas leur variables, autrement dit, un module ne peut utiliser une variable globales d’un autre module.

// user.js
let user = "Jules"

// main.js
console.log(user) //  

//index.html
<script type="module" src="user.js"></script>
<script type="module" src="hello.js"></script>

la console va afficher "SyntaxError: The requested module './lib.js' does not provide an export named 'user'"

//on modifie dans user.js
export let user = "Jules"  // correct

//Pour illustrer directement dans une page HTML
<script type="module">
    export let user = "Jules"
</script>
<script type="module">
    console.log(user)  // Erreur,et on ne peut faire import du module puisqu'il est inline, la seule façon urait été de faire window.user = "Jules"
</script>

Evaluation une seule fois du module à l’import

//index.html
<script type="module">
    window.test = 0
</script>
<script type="module">
    import { val } from './util.js'

</script>
<script type="module">
    import { val } from './util.js'

    console.log(val)
</script>

// util.js
window.test++
export let val = window.test

bien que l’on ait importé deux fois le module util.js, le code va afficher 1 et non 2

Prenons un autre exemple, qui va montrer que cet aspect est en fait pratique, permet aux différents scripts de se « partager  » une variable

// config.js
export let config = {};

// 1.js
import { config } from './config.js'
config.user = "user1"

//2.js
import { config } from './config.js'
config.url = "http://local.test/user1"

// index.html
<script type="module" src="1.js"></script>
<script type="module" src="2.js"></script>
<script type="module">
    import { config } from './config.js'
    console.log(config)
</script>

L’exemple ci-dessus montre que les fichier 1.js et 2.js apportent des nouvelles propriétés

La propriété meta de import

// en reprenant l'exemple précédent

// index.html
<script type="module" src="1.js"></script>
<script type="module" src="2.js"></script>
<script type="module">
    import { config } from './config.js'
    console.log(import.meta)
</script>

On obtient quelques informations sur le module courant.

Le mot clé this dans un module

Dans un module le mot clé this n’existe pas, il est undefined

<script>
  alert(this); // window
</script>

<script type="module">
  alert(this); // undefined
</script>

Les script module sont deferred dans un client

Ce qui suit n’est valable que dans un navigateur

Vous connaissez l’attribut defer dans la balise script.

<script type="module">
  alert(typeof button); // ce module est defer, il se télécharge immédiatement, mais il attend que le HTML finisse de se télécharger avant de s'exécuter.
</script>


<script>
  alert(typeof button); // s'exécute immédiatement après le téléchargement, avant que le HTML du bouton ne vienne
</script>

<button id="button">Button</button>

Toujours préciser le chemin,

Même si le script est adjacent à votre page HTML, il faut préfixer avec un point slash

import {compteur} from 'util.js'; // dans un navigateur ceci n'est pas autorisé,TypeError: Failed to resolve module specifier "util.js". Relative references must start with either "/", "./", or "../".
import {compteur} from './util.js'; 

scripts externes et CORS

L’inclusion de script externe en tant que module doit se faire avec précaution dans le cas des modules. Pour les scripts externes de même origine que la page HTML, pas de soucis, mais si le script vient d’un autre nom de domaine, alors le serveur d’origine

<script type="module" src="https://site.com/script.js"></script>
il faut que site.com ait activé le CORS.

async et les modules

Le mot clé async permet de télécharger en parallèle un script javascript externe, mais ne s’utilise pas pour les script inline (à même la page HTML). dans le cas des modules, le mot clé async s’applique aussi aux Javascript inline.

async Javascript import
<script async type="module">
  import {counter} from './utils.js';

  counter.count();
</script>

Async, defer, preload, prefetch : tout savoir sur ces attributs

Ces attributs lorsqu’il sont utilisés sur une page HTML on tous un point commun : ils permettent de télécharger de façon asynchrone les fichiers liés depuis la page web, dans le but d’accélérer le téléchargement de la page et son exécution.

Async et Defer s’appliquent à la balise <script> uniquement

Comment utiliser ces deux attribut dans un script ? voici un exemple

<script src="init.js" defer></script>

ou 

<script src="init.js" async></script>

Quelle est la différence entre async et defer? Regardez les deux schémas tirés de ce site,

async ne vas pas attendre la fin du téléchargement de la page HTML pour s’éxécuter, alors sur defer va attendre la fin du téléchargement de la page HTML (le parsing plus exactement).defer va être utile si vous avez besoin que tous les éléments du DOM soient mis en place.

Preload et Prefetch sont pour les balises <link>

<link rel="preload" href="style/style.css">
<link rel="preload" href="main.js">

preload permet de télécharger de façon asynchrone les ressources indiquée (css, javascript), mais il ne sont pas exécutés. Ils seront exécutés par le navigateur en cas de nécessité, ce qui est une bonne forme d’optimisation. Pour en savoir plus sur les détails techniques, cette page de la documentation officielle sur MDN.

Prefetch pour pré-télécharger des pages web

<link rel="prefetch" href="/articles/" as="document">

Cet attribut a pour but de prétélécharger une page (même si on ne va pas la visiter) pour que lorsqu’on clique sur le lien, on n’ai pas à la télécharger. Il faut bien sûr s’assurer que c’est une page souvent visitée. La page prétéléchargée sera dans le cache prefetch.

L’image ci-dessus explique le fonctionnement (source : lien de la source).

Docker denied: requested access to the resource is denied

The push refers to repository [docker.io/yvonhuynh/hello2]
An image does not exist locally with the tag: yvonhuynh/hello2

Si vous venez de commencer récemment à suivre un tutoriel Docker et que vous avez ce message, alors il y a peut être deux raisons, la seconde raison est plus probable.

Le contexte :

vous avez construit une image docker et vous aimeriez la pousser ver le hub de docker, mais vous rencontrez ce message lorsque vous poussez votre image soit depuis le Docker Desktop, soit depuis la ligne de commande.

Le dockerfile:

FROM node:alpine
COPY . /app
WORKDIR /app
CMD node app.js

Le projet javascript:

console.log('bonjour version 2')

Première raison possible :

Vos identifiants de connexion ne son tpas les bon

Cette raison est à titre préventif, il est fort à parier que ce n’est pas le cas.

docker logout
docker login
// renseignez vos identifiants.

Seconde raison possible:

Vous n’avez pas donné un tag à votre image

Vous avez buildé une image avec la commande:

docker build -t hello2
docker tag nom_image NOM_DOCKERHUB/nom_image



docker tag hello2 yvonhuynh/hello2


docker push  yvonhuynh/hello2

Le nom de l’image s’appelle hello2 dans le hub docker.

yvonhuynh est le login de votre registry docker sur le site docker.com (dans notre cas mais il existe d’autres registry). un registry est comme un repository git.

Quelle différence entre le Node et les Elements dans le DOM?

lorsque vous faites des requêtes sur les éléments d’une page HTML et en faisant un affichage des éléments que vous avez obtenus, parfois vous avez des nodelist et parfois vous avez des Element, cela peut prêter à confusion parce que à première vue ce sont deux choses qui sont similaires, mais alors si elles sont similaires pourquoi elle portent deux types différents?

Node (Nœuds) dans le DOM

Dans le DOM, tout est un nœud. Un nœud peut être un élément, un attribut, un texte, un commentaire, un document, ou tout autre type d’objet DOM. Les nœuds sont organisés dans une structure arborescente, avec le nœud de document en haut et tous les autres nœuds en découlant.

Les nœuds ont des propriétés et des méthodes qui vous permettent de les manipuler ainsi que leurs nœuds enfants. Par exemple, vous pouvez utiliser la méthode appendChild() pour ajouter un nœud enfant à un nœud existant.

Éléments dans le DOM

Les éléments sont un type spécifique de nœud qui représente un élément HTML ou XML. Les éléments ont toutes les propriétés et méthodes d’un nœud, mais ils ont également des propriétés et des méthodes supplémentaires qui leur sont propres.

Par exemple, les éléments ont une propriété tagName qui spécifie le nom de l’élément, tel que « div » ou « span ». Les éléments ont également des attributs, qui peuvent être accédés en utilisant la méthode getAttribute() ou simplement en accédant à l’attribut comme une propriété de l’élément.

Il existe 12 types de noeuds

Source StackOverflow

Element est un type de noeud, il y a aussi les noeuds TEXT, COMMENT, etc. Mais le plus familier pour nous est le type Element, les éléments de la page HTML.

Quand obtient l’un ou l’autre?

Lorsque vous faite une requêtes qui peut retourner plusieurs item, vous obtenex un NodeList (ce n’est pas un tableau !)

    <div class="container">
        <ul id="liste">
            <li id="un">Un</li>
            <li id="deux">Deux</li>
            <li id="trois">Trois</li>
        </ul>
    </div>


let el = document.getElementById('un')   
console.log(el)  // retourne un Element

let nodelist = document.querySelectorAll('li')
console.log(nodelist)   // retourne un nodeList

Dans l’exemple ci-dessus, même si le second exemple retourne un NodeList, les items de cette nodelist sont des Elements ! Vous pouvez le vérifier avec instanceof

console.log(nodelist[0] instanceof Element)  // true
console.log(nodelist[0] instanceof Node)  // true
console.log(el instanceof Node)  // true

Mais alors pourquoi dans ce cas on ne fait pas un objet ElementList? c’est un choix des ingéieurs qui ont fait Javascript.

Par contre HTML5 définit un objet HTMLCollection, qui est un objet qui ne contient que des Element, qui exclut tous les autres types de noeuds. Vous pouvez voir HTMLCollection à l’oeuvre avec l’exemple suivant:

let el = document.getElementById('liste') 

En pratique vous n’avez pas à vous soucier de ces subtiles différences.

Je participe à un projet Hyperledger

Cela fait pas mal de temps que je voulais m’investir dans un projet blockchain, à défaut de blockchain publique, je vais travailler sur une blockchain privée, et c’est sur Hyperledger.

Le type de projet de tokenization immobilière, thème très en vogue actuellement. Le but est de permettre une plus grande liquidité dans l’immobilier et de dépoussiérer les pratiques en vigueur.

RealtyKey.io est la plateforme web2 pour aborder le web3.

Principe de RealtyKey

Le principe est un principe d’investissement. Jusque là rien de différent, là où ça commence à se différencier de la finance traditionnelle, c’est que vous achetez un certificat d’investissement, appelé NFT et ce après avoir créé un compte (vous obtenez un AccountKey), vous détenez un NFT InvestKey prouvant que vous êtes inscrit . Ce dernier NFT vous permet de minter deux autres types de NFT, les Incomekey qui vous permettent de toucher un loyer du bien dans lequel vous avez investi, et les RealtyKey qui vous permettent d’habiter quelques temps dans un bien dans lequel vous avez investi.

La stack technique

La blockchain Hyperledger est contenue dans un Kubernetes, un orchestrateur de conteneurs Dockers. Le langage de développement de la blockchain, celui utilisé pour écrire les smart contracts appelé chaincode dans Hyperledger, est le langage Go, assez facile à appréhender.

Hyperledger expose un webservice directement consommable via Postman par exemple.

C’est assez différent de ce que je connais sur les blockchains publiques, notamment Ethereum et autres EVM compatibles. Le langage pour faire les smart contracts pour les chaine EVM est Solidity , que certains disent assez proche de Javascript, ce que personnellement je ne trouve pas, c’est assez unique.

Les différences entre une blockchain privée et une blockchain publique

Le choix d’une blockchain privée est motivée par les raison suivantes (non exhaustives)

  • pas de notion de consensus
  • milieu confiné à priori non sensible aux hacks
  • grand TPS
  • beaucoup plus sécurisée

Pourquoi utiliser la blockchain pour investir de façon fractionnée dans l’immobilier?

La blockchain permet de tout tracer sans pouvoir de falsification. Le fait de tokéniser rend l’actif plus liquide, et améliore la facilité de trouver une contrepartie quand on veut vendre.

Meilleure liquidité

Le ticket d’entrée bas permet à tout le monde de participer à la possession d’un bien. Vous n’avez pas à passer les étapes du notaire, DPE, etc. Tout est fait, vous n’avez qu’à entrer dans un investissement après avoir obtenu votre account key. L’opération ne prend que 15 minutes.

Meilleure contrepartie

L’apport de liquidités et l’ouverture à un marché financier cryptonatif plus vaste permettra une plus grande participation d’acteurs (idéalement particuliers comme institutionnels), concrètement si vous ne voulez plus investir dans un bien, où que vous voulez arbitrer vers un autre bien, et que vous voulez vendre vos parts, vous trouverez plus facilement un acheteur pour vos parts.

Ceci est à mettre en opposition avec un produit comme la SCPI, où il est notoire que les conditions de sortie sont plus délicates et de loin (clause de sortie, illiquidité du produit).

Processez des fichier CSV avec AWK pour faire des requêtes SQL

AWK est un utilitaire du monde UNIX et Linux, très puissant et très rapide, très adapté dans le traitement de ligne.

awk -F "," '{print "INSERT INTO table (last_name,first_name,email) VALUES ('\''"$1"'\'', '\''"$2"'\'','\''"$3"'\'');"}' test.txt

Les difficultés relatives à AWK

J’ai rencontré les plus grandes difficultés lorsque j’ai dû concaténer les colonnes du fichier texte. En effet dans une requêtes SQL de type INSERT, il y les valeurs à entourer d’apostrophe dans le fichier de sortie, donc il faut échapper le caractère apostrophe, et ce n’est pas un simple backslash, car le backslash doit être échappé par une apostrophe pour être reconnu comme un backslash d’échappement !

  • https://unix.stackexchange.com/questions/593212/awk-print-apostrophe-single-quote
  • https://earthly.dev/blog/awk-csv/
  • https://www.tutorialspoint.com/awk/awk_basic_examples.htm
  • https://www.geeksforgeeks.org/awk-command-unixlinux-examples/

Scraper le web avec Selenium en Python

Contexte : je suis sous MacOS en architecture Intel 64bit (processeur Core i5)

Selenium vous permet programmatiquement d’émuler un navigateur (ici ce sera Chrome), et de télécharge une page et de l’analyser. Pourquoi est ce que c’estintéressant? Pour les test d’interface d’une part, puis d’autre part si vous voulez scraper une page très javascript, où les éléments sont rendus en JS donc non récupérables par un scrap avec BeautifulSoup, Selenium le peut.

Il est juste plus lent que BeautifulSoup, en effet il se repose sur le navigateur Chrome (notre cas ici mais ça peut être n’importe quel navigateur) qui fait le rendu, alors que BeautifulSoup ne fait pas le rendu.

Pré-requis :

  • connaissez votre version de navigateur Chrome, en allant dans les 3 petits point puis Aide puis A Propos de Google Chrome
  • Déterminez l’architecture de votre plateforme
  • Ensuite allez sur le site de Chromium pour télécharger le driver à mettre dans le même répertoire que votre script, le driver permet à Selenium de contrôler Chrome, c’est dépendant de l’OS et du microprocesseur. Il faut suivre le lien pour les dernières versions de Chrome
  • Installer la librairie Selenium pip install selenium

Script minimal de Selenium pour scraper une page

from selenium import webdriver
from selenium.webdriver.common.keys import Keys

browser = webdriver.Chrome()   #mac https://stackoverflow.com/questions/76928765/attributeerror-str-object-has-no-attribute-capabilities-in-selenium
#driver = webdriver.Chrome('./chromedriver'). #ne marche pas sur Mac

browser.get("https://www.python.org")

print(browser.title)

Faire marcher LOAD DATA INFILE dans mysql 8

Pourquoi utiliser LOAD DATA INFILE ?

Cette commande permet de charger des données dans une table de votre base de données à une très grande de vitesse. J’ai pu par exemple insérer 500000 lignes en seulement 7 secondes. C’est quelque chose à considérer si vous avez de grosses bases à reproduire dans un environnement. En effet, par le passé, j’ai travaillé dans une entreprise où il fallait attendre 6h pour mettre en place la base de données, je pense que avec cette méthode on pourrait très bien ramener à moins de 10 minutes. Attention, le chargement est très rapide pour une table donnée, je ne tiens pas compte du temps de passage entre une table et une autre, mais normalement il n’y a pas de problème.

La méthode la plus souvent utilisée pour insérer un fichier dans une table, et de faire un fichier constitué de clause insert, cette méthode n’est pas très rapide en effet si vous avez plusieurs lignes insert mais c’est plusieurs requêtes qui sont faites cependant on peut optimiser en faisant un insert multiligne, mais même ça ça ralentit le chargement des données. En effet le fait d’exécuter une requête SQL va faire intervenir le moteur de base de données, qui doit lire la requête SQL l’interpréter l’exécuter et mettre en mémoire les choses.

Avec cette méthode, en outre passe l’exécution de requête SQL, en effet par exemple lorsque en charge un fichier CSV, il n’y a pas de requête SQL qui est faite, on injecte les données simplement colonne par colonne, ligne par ligne. Il n’y a donc pas d’exécution de code SQL, c’est une insertion brute, c’est pour ça que c’est aussi rapide.

Dans vos bases de données de développement, par exemple e-commerce, vous pouvez vous permettre d’utiliser une méthode traditionnelle, mais si vous êtes amené à travailler avec de grosses bases de données, comme un Big Data, analyse des données, vous avez de très grosses bases de données, à reconstituer dans la base de données. Cette méthode est donc très avantageuse.

Le problème avec cette fonction

Le problème avec cette fonction c’est qu’il est assez difficile de le paramétrer, en particulier avec mysql8. Le problème est encore plus ardu lorsque vous utilisez PHPmyadmin pour faire l’insertion de données. En effet, on intercalant entre le fichier et la base de données un logiciel écrit en PHP donc pas forcément très performant, vous augmentez les chances que cela ne marche pas. Je vous recommande donc de faire l’insertion depuis la ligne de commande. Même malgré ça nous allons rencontrer quelques difficultés.

Nous allons prendre un exemple simple, avec un petit fichier test.csv de quelques lignes, nous allons surtout nous concentrer sur la partie amont de l’insertion.

id,nom,prenom,email
1,"Dupont","Alex","dupont.alex@gmail.com"
2,"Dupont","Danielle","dupont.danielle@gmail.com"
3,"Durand","Ines","durand.ines@gmail.com"
4,"Durand","Hugo","durand.hugo@hotmail.com"
5,"Camus","Albert","albert.camus@gmail.com"

Nous allons disposer d’une table dont voici le code

CREATE TABLE `test` (
  `id` int NOT NULL,
  `nom` varchar(50) NOT NULL,
  `prenom` varchar(50) NOT NULL,
  `email` varchar(150) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

Première tentative d’insertion dans PHPMyAdmin

Pour information la documentation officielle traitant de cette fonction se trouve sur cette page. La doc officielle dit aussi que MySQL doit avoir les privilèges sur les fichier à importer (logique), par exemple il doit avoir les droit de lecture sur le fichier test.csv.

LOAD DATA LOCAL INFILE 'E:\OneDrive\formapedia_cours\coursSQL\load infile\test.csv' INTO TABLE test
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 1 LINES
(id, nom,prenom,email);

Ceci se solde par une erreur « #2068 - LOAD DATA LOCAL INFILE is forbidden, check related settings like mysqli.allow_local_infile|mysqli.local_infile_directory or PDO::MYSQL_ATTR_LOCAL_INFILE|PDO::MYSQL_ATTR_LOCAL_INFILE_DIRECTORY« . Ce qui est embêtant car je vois PDO donc j’imagine que ce doit être un réglage PHP dans PHPMyAdmin, il va falloir bidouiller le fichier de configuration. Je vois aussi qu’il y a un réglage dans le fichier de configuration de MySQL. Recherches infructueuses. Ce que je vois est qu’il faut mettre une variable de configuration dans my.ini (Windows) ou my.cnf (Linux)

[mysql]
local-infile=1

[mysqld]
...
local_infile=1 <<  parfois je vois local-infile=1  (avec le trait d'union)

[mysqldump]
quick
max_allowed_packet=512M

En redémarrant le serveur, je n’ai pas plus de succès…

Je tente de passer par MySQL en ligne de commande

En fait je fais ça, car je sais que PhpmyAdmin introduit une couche de configuration rendant la tâche plus délicate. Je passe donc par la ligne de commande qui est plus simple. En se connectant en ligne, n’oubliez pas de sélectionner la base de donnée avec use.

En collant le texte ci-dessous: Attention \r\n dans le système Windows mais \n tout court pour Linux

LOAD DATA LOCAL INFILE 'E:\OneDrive\formapedia_cours\coursSQL\load infile\test.csv' INTO TABLE test
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 1 LINES
(id, nom,prenom,email);

J’ai a réponse suivante:

ERROR 2068 (HY000): LOAD DATA LOCAL INFILE file request rejected due to restrictions on access.

C’est donc une question de restriction de lecture de fichier, mais je suis sous Windows, je ne m’inquiète pas trop (Linux est plus subtile), j’essais néanmoins de placer le fichier csv dans le répertoire où se trouve la base de données (dans le répertoire d’installation de MySQL, chaque base de données correspond à un fichier binaire, dans un répertoire de même nom (dans le cas de InnoDB). Mais j’ai toujours le même problème.

La révélation

c’est alors que je tombe sur cette page qui me dit de me connecter avec un argument:

mysql --local-infile=1 -u root -p

Et voilà j’ai pu loader le csv sans problème !

Mettre en place un environnement Python dans VSCode

CTRL + SHIFT + P puis Python: Create Environment et sélectionnez la commande.

Choisissez l’interpréteur

Ensuite ça prend un peu de temps pour créer l’espace virtuel, isolé de développement Python.

Pour s’assurer de la version de l’interpréteur Python, dans la palette de commande (CTRL + SHIFT + P) puis Python: Selectg Interpreter

Maintenant vous êtes prêt pour commencer à code en Python dans VSCode.

Créez un fichier hello.py,

print("hello")

Pour exécuter, appuyez sur le triangle en haut à droite de VSCode, liste déroulante choisir Debug Python File. Notez le point rouge qui est le point d’arrêt.

Debugger python sous VScode

Il suffit de mettre un pint d’arrêt et de cliquer sur le symbole debug dans la barre latérale. Une autre méthode est d’utiliser le menu contextuel et démarrer le script.

Pour plus d’information ce lien.

Quand utiliser Redux

Quand ne pas utiliser Redux

  • budget peu élevé
  • application de taille petite à moyenne
  • UI simple, flux de données simple
  • données statiques

Pours et contres de Redux:

Pour :

  • débug facilité
  • test
  • faire et défaire
  • persistence de state
  • préserve le state d’une page

Contre:

  • complexité
  • verbosité

Débuter avec Greasemonkey

C’est un plugin navigateur qui vous permet d’exécuter du code Javascript sur la page où vous vous trouvez. Vous connaissez les extensions de navigateur? Greasemonkey vous permet facilement de retrouver les mêmes capacités sans avoir à coder toute l’extension.

Installez Greasemonkey version 4 (sur Chrome c’est TamperMonkey).

Pour aller au plus simple, cliquez sur l’icône du singe, vérifiez qu’il est bien activé puis cliquez sur Nouveau Script

// ==UserScript==
// @name     Anonyme Script 813510
// @version  1
// @grant    none
// ==/UserScript==


Sion on veut de l'ajax (cross domain en plus !)
//@grant GM.xmlHttpRequest

Vous pouvez personnaliser les annotations, @name pour le nom du script, @version qui n’aura pas d’influence sur votre scripts, et surtout @grant est très important, si vous voulez par exemple faire une requêtes Ajax, par défaut vous ne pouvez pas. Je vous joins la page de documentation du plugin:

wordpress cURL error 60: SSL certificate problem: unable to get local issuer certificate

Récemment j’ai eu droit en voulant activer mon thème Astra, je me suis heurté à l’erreur suivante :

cURL error 60: SSL certificate problem: unable to get local issuer certificate

Ce n’est pas un problème du thème Astra

Ce n’est pas un problème de votre hébergeur

J’ai fait 2 aller retours vers le hébergeur 02Switch, qui est très bien, réactif, mais n’a pu me solutionner mon problème, se contentant de dire que chez eux tout va bien. Bien sûr je ne savais pas quel était le vrai problème, je sentais que c’était un problème du serveur du site, et pas de celui d’Astra, comme me le suggérait l’hébergeur.

La solution est ailleurs

Ce n’est seulement qu’après avoir vu la petite vidéo d’Astra montrant que (tout plugins désactivés, le problème persistait, en effet en allant sur Outil > Santé du site, il y avait une alerte d’erreur critique concernant REST API, en gros les appels vers d’autres serveurs échouaient à cause du certificat.

En googlant un peu plus sur Internet, j’ai trouvé la solution sur cette page :

Le problème était que le certificat de WordPress, le fichier ca-bundle.crt localisé dans wp-includes/certificates, datait de 2016…

La solution consistait à télécharger un certificat plus récent sur ce lien le fichier ca-cert.pem. Le soucis c’est que ce fichier ne nommait pas pareil, pas de panique il suffit juste de copier le contenu texte de ce fichier et de remplacer l’ancien contenu intégralement du fichier ca-bundle.crt et le tour est joué.

Se familiariser avec l’API Binance

Maintes fois j’ai tâté de l’API Binance, entrecoupé de période d’inactivité, et quand je revenais, je devais tout réapprendre, ayant tous oublié…

Ce post est une tentative de mémorisation de cette API, mes besoins pour le moment concernent l’order book et les kline (chandelier japonais).

La page d’entrée de la documentation API Binance.

Order book

L’order book est le livre des ordres de bourse à cours limité, ce sont des ordres en attente d’exécution, à la différence des ordres market, que vous ne verrez pas car ils sont passés immédiatement.

Les données vous permettent de tracer la profondeur de marché, de voir la densité des ordres en fonction du cours d’exécution

Quotation en temps réel à différents timeframes

Cette fonctionnalité est primordiale pour tracer les cours des crypto, on a besoin en version OHLC, pour tracer les bougies et faire de l’analyse technique dessus.

Installer le framework Express pour nodeJS

Ce tuto montre comment bootstraper un projet NodeJS avec la base de donnée MySQL

Installation de NodeJS et NPM

Pour l’installation, il suffit de télécharger NodeJS (la dernière version) NPM sera disponible automatiquement.

Installation de Express

Express est un framework MVC fait pour NodeJS, c’est l’équivalent de Symfony pour le PHP. Créez un répertoire pour votre projet

npm init   // pour initialiser le package.json

Installation des packages pour le projet

npm i dotenv
npm i express
npm i mysql

Code minimal pour se connecter à la base de donnée:

On veut vérifier avant de continuer plus loin que la connexion se fait sans problème.

#index.js
const express = require('express')
const app = express()
require("dotenv").config()
var mysql = require('mysql');


app.get('/', (req, res) => {
    res.send('Hello World!')
})

app.listen(port, () => {
    console.log(`Example app listening on port ${port}`)
})

const DB_HOST = process.env.DB_HOST
const DB_USER = process.env.DB_USER
const DB_PASSWORD = process.env.DB_PASSWORD
const DB_DATABASE = process.env.DB_DATABASE
const DB_PORT = process.env.DB_PORT

var con = mysql.createConnection({
    host: DB_HOST,
    user: DB_USER,
    password: DB_PASSWORD,
    port: DB_PORT
});
con.connect(function (err) {
    if (err) throw err;
    console.log("Connected!");
});

Code générique pour requêter dans une table

var con = mysql.createConnection({
    host: DB_HOST,
    user: DB_USER,
    password: DB_PASSWORD,
    port: DB_PORT
});
con.connect(function (err) {
    if (err) throw err;
    console.log("Connected!");
});

Installer nodemon pour recharger au changement de fichier

npm install --save-dev nodemon
pour lancer avec nodemon à la place de node
./node_modules/.bin/nodemon index.js

Débugger sous NodeJS avec VSCode

VSCode est vraiment l’éditeur de code couteau suisse, son avantage est que s’il y a un langage même confidentiel, vous trouverez un plugin. Même s’il n’est pas le meilleur dans chaque catégorie, par exemple je trouve PHPStorm mieux pour le PHP, il est un très bon outil gratuit de surcroît.

Pour NodeJs, c’est un peu plus simple que pour Xdebug en PHP, il y ap lusieur façons de déclencher le débugger de NodeJS, on va voir l’auto déclenchement du débugger dès qu’un process node est en cours d’exécution.

Je suppose que vous ayez un fichier index.js, à exécuter via la commande shell, pour le lancer vous faites node index.js. Si vous ne l’avez pas fait vous pouvez démarrer un projet NodeJS.

Et là vous devrez avoir la barre de status orange, indiquant que le débug est actif.

Configuration de VSCode pour le debug automatique (Auto attach)

Pour activer l’auto attach, faites la combinaison de touche : SHIFT + CTRL + P, une boite de recherche va apparaitre, tapez « Toggle Auto Attach » pour trouver l’option et cliquez dessus.

Maintenant chaque fois que vous démarrez un process NodeJS, VSCode se met en mode debug.

Retour en haut