[PouchDb] [Cloudant] [Vue.js]Un premier Formulaire avec la Technologie Cloud d’Ibm.

pouchcloudanttéléchargé

Introduction


Une base de données hébergée dans le cloud permet, entre autre, les avantages suivants : Synchro temps réel entre les navigateurs, réplication de bdd, bases de données gigantesques, Recherche avancée avec Elastic Search(indexation…), Map reduce … Bref, ici on va seulement déjà faire tourner une base de donnée CouchDb, avec la librairie additionnelle PushDb qui prends le relais en local au cas ou le matériel perde la connexion.

L’interface Homme Machine tourne en html 5  avec le framework Front-End vue.js et la librairie Bootstrap vue v4.0, du coup on a encore besoin d’un bon vieux serveur apache, sur free.fr, par exemple … Pour cet exo, pas de test tablette, seulement du  22 pouces.

lapin.jpg « Et pourquoi que tu fais un exercice comme ça, petit jojo ? « 

Parce que, selon la doc, PouchDb permet d’envisager la création d’applications complexes synchronisées, sur tablettes, qui résistent au mode offline, tout cela avec la syntaxe js, toujours relativement simple et cohérente, surtout avec le framework vue.js.

De plus, en 2016 J’avais déjà expérimenté AngularJs + Firebase et ç’était vraiment trop bon avec le 3 way binding, seulement le défaut de Firebase est qu’il est exclusivement hébergé online, alors que couchDb peut être installé sur des serveurs persos, et tourne avec jquery, node.js, et SURTOUT en mode no-serveur, avec pourtant une authentification, car c’est aussi un serveur web ! Ce qui signifie se passer de code serveur , et ça pour les pré-maquettes, c’est seulement génial, quoi , petit !

lapin2.jpg « Ouf, ca va, je suis rassuré maintenant, grouinch, galop.. galop . »

Pourquoi IBM Cloudant ?


Aws et Google Cloud demandent la carte bleue, par contre Ibm Cloudant est gratuit pour l’instant, en tout cas ça marche bien, avec juste des limitations de connexion.

Pour la méthode, ben, ne pas prendre peur, créer son compte ibm cloudant, puis créer sa base noSql de type cloudant. Je n’ai pas le temps d’expliquer ça là. Il faut juste choisir une base de données couchDb bien sur.

cloudant.jpg

Ensuite il y a  choses importantes à faire pour cet exo :

  1. Générer les données d’identification, qui va permettre à notre app HTML de se connecter à la base couchDb :cloudant2.jpg

2. Activer CORS afin que l’on se serve de couchDb directement à partir du front end en JS  (Bien sur, ce n’est pas safe, pour l’instant,( hi hi )!

cors

 lapin4.jpg « Bon ça y est , c’est fini ?  groinch « 

La Micro App


On va élaborer un humble formulaire repris de ce post, qui nous permet d’entrer un email, une plaque d’immatriculation, et une marque, un peu à la manière de l’ANTS . bref c’est pour entrer un véhicule dans une bdd noSql couchDb

Le temps global de dev est approximativement de 30- 45 minutes , parce qu’on découvre, mais bien sur, ça se réduira ensuite.

Pour l’instant, pas d’authentification couchDb, on commence simple … L’update n’est pas là non plus …

PouchDb


pouchDb est vraiment trop intéressant, en effet c’est ,pour résumer au maximum et approximativement,  une réplique locale de la base de données couchDb, qui attends puis synchronise automatiquement en cas de coupure réseau. Autrement dit, quelque chose d’extrêmement intéressant  pour le matos mobile !

Tester le super formulaire… one Agaaain


Je l’ai mis sur free : http://nicolas.huleux.free.fr/pouchdb/indexvuepouch.html

puhhh

Le Manifest pour forcer la mise en cache


Se renseigner sur l’ajout de Manifest dans la balise HTML , qui force la mise en cache d’une app web … Je l’ai mise sans plus d’analyse pour l’instant.

Le fichier Index.html


Il charge les libs en début et fin de script.

Il charge donc également le fichier app.js qui contient notre code vue.js

Dans le HTML , on voit les champs input, et la liste des automobiles qui provient de couchDb .

<html manifest="example.appcache">
	<head>
	<meta http-equiv="X-UA-Compatible" content="IE=8" />
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Exo pouchdb</title>
        
		<!------------------------------------------------------------ CHARGEMENT DES LIBRAIRIES ------------------------------------------------>
		
		<!-- Bootstrap et Bootstrap vue. -->
		<link type="text/css" rel="stylesheet" href="bootstrapvue/bootstrap.min.css" />
		<link type="text/css" rel="stylesheet" href="bootstrapvue/bootstrap-vue.css" />
		<script src="bootstrapvue/vue.js"></script>
		<script src="bootstrapvue/polyfill.min.js"></script>
		<script src="bootstrapvue/bootstrap-vue.js"></script>
		
		<!-- JQuery. -->
		<script src="js/jquery.min.js"></script>
		<!-- PouchDb. -->
		<script src="js/pouchdb.min.js"></script>
		<!-- Vuepouch plug in.  https://github.com/sadick254/vuepouch -->
		<script src="js/vuepouch.js"></script>
		

		
		<!------------------------------------------------------- FIN DE CHARGEMENT DES LIBRAIRIES ------------------------------------------------->
	</head>

<!-- La page HTML générale -->

<body style="background-color:tan" >
	<div id="app"  >
	<div class="row" style="margin:5%">
		<div class="card " style="width:50%;">
			<div class="card-header">
				<h5 class="card-title">Formulaire</h5>
			</div>
			<div class="card-body">
				<b-form @submit="onSubmit" @reset="onReset" v-if="show">
					<b-form-group id="exampleInputGroup1" label="Votre email:" label-for="exampleInput1" description="Nous ne partageons pas votre email avec quelqu'un.">
						<b-form-input id="exampleInput1" type="email" v-model="form.email" required placeholder="Entrez l'email"> </b-form-input>
					</b-form-group>
					<b-form-group id="exampleInputGroup2" label="La plaque de votre véhicule:" label-for="exampleInput2">
						<b-form-input id="exampleInput2" type="text" v-model="form.plaque" required placeholder="Entrez la plaque d'immatriculation"> </b-form-input>
					</b-form-group>
					<b-form-group id="exampleInputGroup3" label="La marque:" label-for="exampleInput3">
						<b-form-select id="exampleInput3" :options="marques" required v-model="form.marque"> </b-form-select>
					</b-form-group>
					<b-form-group id="exampleGroup4">
						<b-form-checkbox-group v-model="form.checked" id="exampleChecks">
							<b-form-checkbox value="email">Envoyez moi un email</b-form-checkbox>
							<b-form-checkbox value="sms">Envoyez moi un SMS</b-form-checkbox>
						</b-form-checkbox-group>
					</b-form-group>
					<div align="right">
						<b-button type="submit" variant="primary">Enregistrer</b-button>
						<b-button type="reset" variant="danger">Reset</b-button>
					</div>
				</b-form>
			</div>
			<div class="card-footer"></div>
		</div>
		<div class="card " style="width:50%;">
			<div class="card-header">
				<h5 class="card-title">Liste</h5>
			</div>
			<div class="card-body">
				<ul>
					<li v-for="a in  automobiles">{{a.email}}-{{a.plaque}}-{{a.marque}}</li>
				</ul>
			</div>
			<div class="card-footer"></div>
		</div>
	</div>
</body>

<!-- Chargement de l'app -->
<script src="js/app.js"></script>
<!-- Css perso -->
<link type="text/css" rel="stylesheet" href="style.css" />

Le fichier APP.JS


On instantie notre objet vue, et on peut voir qu’il y a un objet pouchDb qui permet de spécifier la base locale pouchDb et la base online couchDb.

C’est grâce à vuepouch (https://github.com/sadick254/vuepouch ), la petite librairie de sadick254 dédiée au pilotage de pouchdb par vue.js, qu’on peut faire ça facilement. Il existe une autre lib plus grosse à tester ces jours ci : https://github.com/QurateInc/vue-pouch-db — (Note : en effet, la synchro n’est pas globale pour l’instant avec cette petite lib, epxlication dans la conclusion.)

Dans la méthode ajouterAutomobile(), on voit qu’on se sert de $pouchdbRefs qui nous permet d’ajouter les automobiles à la base de données pouchDb Locale qui synchronise ensuite avec la base de données couchDb Online.

var app = new Vue({
	el: "#app",
	data() {
		return {
			form: {
				email: '',
				plaque: '',
				marque: null,
				checked: []
			},
			marques: [{
				text: 'Selectionnez',
				value: null
			}, 'Peugeot', 'Renault', 'Citroen', 'Ds'],
			show: true,
			personnes:[]
		}
	},
	pouchdb: {
		vehicules: {
		  localDB: "vehicules",
		  remoteURL: "https://xxxxxxxx-be6e-42d6-b914-d0ecae937981-bluemix:8eeedbe180c1ce90cdc3ae37b9e74af7368b21a37b531aae819929d3405c7d22@1c54473b-be6e-42d6-b914-d0ecae937981-bluemix.cloudant.com/vehicules"
		}
	},
	computed: {
		automobiles() {
		  return this.vehicules.automobiles
		},
		camions() {
		  return this.vehicules.camions
		}
	},
	methods: {
		onSubmit(evt) {
					evt.preventDefault();
					this.ajouterAutomobile(this.form);
				},
		onReset(evt) {
			evt.preventDefault();
			/* Reset our form values */
			this.form.email = ''; 
			this.form.plaque = '';
			this.form.marque = null;
			this.form.checked = [];
			/* Trick to reset/clear native browser form validation state */
			this.show = false;
			this.$nextTick(() => {
				this.show = true
			});
		},
		ajouterAutomobile(form) {
			this.$pouchdbRefs.vehicules.put('automobiles',form)
		},
		addPassenger () {
			this.$pouchdbRefs.vehicules.put('camions', /*your data*/)
		} 
	}
})

 Conclusion


On voit qu’on peut désormais, en désactivant CORS, écrire directement dans la base couchDb.

Si l’on perds l’accès à couchDb et qu’on continue à enregistrer des véhicules, mes petits essais sommaires ont montré que pouchDb attends la reconnexion, puis exécute une à une les requêtes sur la base online (Dans la console, on les voit passer).

Cependant, subsiste un problème, en effet, Contrairement à Firebase+AngularJs, la synchronisation n’est pas temps réelle, c’est à dire que si j’ouvre 2 navigateurs différents, et que j’entre une info dans l’un, je ne la vois pas apparaitre automatiquement dans l’autre (il faut alors appuyer sur F5), c’est donc ce que l’on va tenter de corriger avec l’utilisation du module https://github.com/QurateInc/vue-pouch-db qui semble corriger ce problème.

En effet, pas envie de mettre un timer qui recharge couchDb à intervalle régulière, je pense que le module vue-pouch-db doit le gérer automatiquement.

Publicités

[Vue.js] Controler les champs d’un formulaire avec vue.js, puis les colorer si erreur avec bootstrap-vue.js

téléchargé.jpg

Introduction


Exemple de controle de champs avec vue.js, puis coloration des champs en erreur, avec bootstrap vue .

Etape un : Un formulaire pour app one page (pas de submit )


Tester le code ici .


<html>
	<head>
	<meta http-equiv="X-UA-Compatible" content="IE=8" />
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Exo Vue.Js</title>
        
		<!------------------------------------------------------------ CHARGEMENT DES LIBRAIRIES ------------------------------------------------>
        
		<script src="js/vue.min.js"></script>
		
		<!------------------------------------------------------- FIN DE CHARGEMENT DES LIBRAIRIES ------------------------------------------------->
	</head>

<!-- La page HTML générale -->
<body >
	<form id="app" >
  
  <p v-if="errors.length">
    <b>Corrigez les erreurs suivantes:</b>
    <ul>
      <li v-for="error in errors">{{ error }}</li>
    </ul>
  </p>
  {{success}}
  <p>
    <label for="name">Nom<label>
    <input type="text" v-model="nom" >
  </p>

  <p>
    <label for="age">Age<label>
    <input type="number" v-model="age" min="0">
  </p>

  <p>
    <label for="film">Film favori<label>
    <select  v-model="film">
      <option>Star Wars</option>
      <option>Vanilla Sky</option>
      <option>Atomic Blonde</option>
    </select>
  </p>

  <p  >
    <button type="button" v-on:click="checkForm()" style="float: right;">   ok </button>
  </p>

</form>
</body>

Le code vue.js :

const app = new Vue({
  el:'#app',
  data:{
    errors:[],
    nom:null,
    age:null,
    film:null,
	success:null
  },
  methods:{
    checkForm:function(e) {

     this.errors = [];
      if(!this.nom){
		this.errors.push("le nom est requis.");
		return false;
      };
      if(!this.age ){
		this.errors.push("L'age est requis.") ;
		return false;
	  };
	  if(this.age < 18){
		this.errors.push("L'age est trop bas.") ;
		return false;
	  };

	  this.success = ' Vous avez correctement rempli le formulaire ';
    }
  }
})

Les CSS :

input[type=text], select {
    width: 100%;
    padding: 12px 20px;
    margin: 8px 0;
    display: inline-block;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-sizing: border-box;
}

input[type=number], select {
    width: 100%;
    padding: 12px 20px;
    margin: 8px 0;
    display: inline-block;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-sizing: border-box;
}

input[type=submit] {
    width: 100%;
    background-color: #4CAF50;
    color: white;
    padding: 14px 20px;
    margin: 8px 0;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

input[type=submit]:hover {
    background-color: #45a049;
}

div {
    border-radius: 5px;
    background-color: #f2f2f2;
    padding: 20px;
}

Etape 2 : On veut colorer les champs ou la personne s’est trompé ..

Tester le code ici.

Il faut alors utiliser bootstrap vue :

 

<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8">
	<link type="text/css" rel="stylesheet" href="bootstrapvue/bootstrap.min.css" />
	<link type="text/css" rel="stylesheet" href="bootstrapvue/bootstrap-vue.css" />
	<script src="bootstrapvue/vue.js"></script>
	<script src="bootstrapvue/polyfill.min.js"></script>
	<script src="bootstrapvue/bootstrap-vue.js"></script>
</head>

<body>
	<div id="app">
		<div class="card " style="width: 18rem;">
			<b-form @submit="onSubmit" @reset="onReset" v-if="show">
				<b-form-group id="exampleInputGroup1" label="Email address:" label-for="exampleInput1" description="We'll never share your email with anyone else.">
					<b-form-input id="exampleInput1" type="email" v-model="form.email" required placeholder="Enter email"> </b-form-input>
				</b-form-group>
				<b-form-group id="exampleInputGroup2" label="Your Name:" label-for="exampleInput2">
					<b-form-input id="exampleInput2" type="text" v-model="form.name" required placeholder="Enter name"> </b-form-input>
				</b-form-group>
				<b-form-group id="exampleInputGroup3" label="Food:" label-for="exampleInput3">
					<b-form-select id="exampleInput3" :options="foods" required v-model="form.food"> </b-form-select>
				</b-form-group>
				<b-form-group id="exampleGroup4">
					<b-form-checkbox-group v-model="form.checked" id="exampleChecks">
						<b-form-checkbox value="me">Check me out</b-form-checkbox>
						<b-form-checkbox value="that">Check that out</b-form-checkbox>
					</b-form-checkbox-group>
				</b-form-group>
				<b-button type="submit" variant="primary">Submit</b-button>
				<b-button type="reset" variant="danger">Reset</b-button>
			</b-form>
		</div>
	</div>
</body>

Le code vue :

var app = new Vue({
		el: '#app',
		data() {
			return {
				form: {
					email: '',
					name: '',
					food: null,
					checked: []
				},
				foods: [{
					text: 'Select One',
					value: null
				}, 'Carrots', 'Beans', 'Tomatoes', 'Corn'],
				show: true
			}
		},
		methods: {
			onSubmit(evt) {
				evt.preventDefault();
				alert(JSON.stringify(this.form));
			},
			onReset(evt) {
				evt.preventDefault();
				/* Reset our form values */
				this.form.email = '';
				this.form.name = '';
				this.form.food = null;
				this.form.checked = [];
				/* Trick to reset/clear native browser form validation state */
				this.show = false;
				this.$nextTick(() => {
					this.show = true
				});
			}
		}
	})

Les CSS :

	.card {
		margin: 0 auto;
		/* Added */
		float: none;
		/* Added */
		margin-bottom: 10px;
		/* Added */
		margin-top: 5%;
	}

#45a049, #4caf50, #ccc, #f2f2f2

[Vue.Js ] Choisir des personnes dans une liste, puis les transférer dans une autre

téléchargé.jpg

Introduction


Choisir des personnes ( Des objets JSON …) dans un tableau d’objet JSON, puis les copier dans un autre tableau d’objets JSON, quand on clique sur un bouton .

On voit là que vue.js structure bien et rends la réalisation d’ un truc de ce genre méga facile, fait en 3 minutes !! Pour l’instant, c’est comme du angularJs, mais en plus structuré !

Tester le code en temps réel


Cliquer ici !

Le code :


La vue HTML :

<html>
	<head>
	<meta http-equiv="X-UA-Compatible" content="IE=8" />
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>Exo Vue.Js</title>
        
		<!------------------------------------------------------------ CHARGEMENT DES LIBRAIRIES ------------------------------------------------>
        
		<!-- development version, includes helpful console warnings -->
		<script src="js/vue.min.js"></script>
		
		<!------------------------------------------------------- FIN DE CHARGEMENT DES LIBRAIRIES ------------------------------------------------->
	</head>

<!-- La page HTML générale -->
<body >
	<div id="example-1">
		Liste générale
		<ul >
		  <li v-for="p in personnes">
			<button  v-on:click="choisir(p)" >+</button> {{ p.nom }} 
		  </li>
		</ul>
		
		Liste des personnes choisies
		<ul >
		  <li v-for="c in personnesChoisies">
			{{ c.nom }} 
		  </li>
		</ul>
	</div>
</body>

Le code vue.js :

<!-- Vue.Js -->

var example1 = new Vue({
  el: '#example-1',
  data: {
    personnes: [
      { nom: 'Philippe' },
      { nom: 'Jalok' },
	   { nom: 'William' },
	    { nom: 'Kamel' },
    ],
	personnesChoisies:[]
  },
  methods: {
	choisir: function(p){

		// Etape 1 : on supprime la personne choisie de la liste générale
		this.personnes.splice(
		  this.personnes.indexOf(p), 1
		);

		// Etape 2 : On l'ajoute à la liste des personnes choisies
		this.personnesChoisies.push({ nom:p.nom }); // Ajout à la liste des personnes choisies
	}
  }

})

Solveur Javascript pour recherche opérationelle.

Introduction


Une lib pour solver avec js : https://www.npmjs.com/package/javascript-lp-solver

But :

Développer une app utilisant cette api.
Traiter les pbs de

  • Transport
  • Mélanges
  • Horaires

avec Dual etc …

Traitement Back-end PHP Try Catch avec réception et traitement Front-end AngularJs 1.6.5

INTRODUCTION :


Gérer les TRY CATCH dans les web service PHP, et communiquer le message contenu dans un THROW directement au FRONT END ANGULARJS

Le système de try catch PHP est puissant et pratique à utiliser, en particulier pour les problèmes de droits par exemple mais cela peut être pour n’importe quoi d’autre !!

Seulement, il faut savoir le faire communiquer avec le FRONT END !!

DETAIL


Pour info, voici le look d’une méthode de ma classe, dans le BACKEND PHP, fichier backend.php , qui enregistre les permissions, on peut voir un test de permission avant l’exécution  de la requête qui enregistre un tableau d’objets JSON décomposé grâce au foreach, l’important ici est de regarder le THROW et son message STRING ( Les contrôles de sécurité ne sont pas affichés dans cet exemple):

    /**
     * Update Permissions after front end management
     * 
     * Required Permission : $_SESSION['auth'] = true | UPDATE_PERMISSIONS
     * 
     * @return true
     * 
     * 
     */
    public function updatePermissions($jsonArray)
    {

        // Check User Permissions
        if (!$this->has_permission('UPDATE_PERMISSIONS', $_SESSION['permissions'])) {
            throw new Exception("Error : You are restricted to do this.");
        };


        foreach ($jsonArray as $index => $row) {
            $row                      = $this->escape_data_object($row);
            $id                       = $row['id'];
            $name                     = $row['name'];
            $permissions              = strval($row['permissions']);
           

            $sql = "UPDATE Job SET name='$name',permissions='$permissions' WHERE id = '$id'";
            $this->conn->query($sql);

        }

        mysqli_close($this->conn);
        return true;
    }

Le Web Service en PHP qui catche une éventuelle erreur de permission et renvoie le message d’erreur STRING du THROW pour pouvoir l’afficher directement dans le front end, (json_encode() permet de créer du JSON, compréhensible par angularJs, ou vue.js, ou angular 5 …) :

 if(!empty($_POST["updatePermissions"]) AND $_SESSION['auth']) {

    $jsonArray = $_POST["updatePermissions"];
    $jsonArray = json_decode($jsonArray,true); 
    $status = 200;

    try{
        $db->updatePermissions($jsonArray);
    } 
    catch(Exception $e){
         echo json_encode(array("response"=>$e->getMessage()));
         exit();
    } 

    echo json_encode(array("response"=>$status));

} 

Et enfin le FRONT END en ANGULARJS 1.6.5 qui traite le message d’erreur dans le .then() et affiche le STRING du THROW originel directement dans le FRONT END (Remarquez que la requête POST ajax s’adressait bien à mon WEBSERVICE en php ):

//~ AJAX CALL -> Sending whole $scope.permissions array of json objects
        $http.post(wsUrl, {updatePermissions: JSON.stringify($scope.permissions)})
            .then(function(data, headers, config) {
                console.log(data.data);

                $scope.ajaxCall = false;
                $scope.record_button_disable = false;

                // Errors management Responses
                if (data.data.response == 200) {
                    Notification.success($translate.instant('REQUESTSUCCESS'));
                    console.log("Réponse Serveur: " + data.data);

                }
                if (data.data.response != 200) {
                    Notification.error(data.data.response);
                }
            })
        }

Et enfin… Juste pour info, la petite fonction privée PHP has_permission() du cru qui permet d’ejecter les gars qui tentent de faire un truc alors qu’ils n’ont pas la permission dans leur array de permissions (séparé par ;), par le back end, hi hi . Par ecemple si il na pas la permission DISPLAY_USERS, le back end throw une erreur, et angularjs(ou vue.js) affiche le message d’erreur:

/**
	 * This function is looking for the permission inside user PHP SESSION permissions variables, if the permission is not present, then it returns false !
	 * 

	 * @return false|true
	 * 

	 */

	private function has_permission($permission, $permissions)
	{

		$permissions = explode(";", $permissions);

		if (in_array($permission, $permissions)) {
			return true;
		}

		return false;
	}

 

240px-Sheep_Shaf_Mouton.JPG‘Bhee, t »es un ouf pourquoi t’utilises pas que Php pour simplifier plutôt que de faire tout ça bhee?’

Parce que AngularJs et Javascript disposent de l’atout temps réel, d’énormément de librairies et sont très puissants, voir par exemple le jeux LARA CROFT http://xproger.info/projects/OpenLara/ avec une lib dédiée graphique js , voir Three.js, ou alors ne serait ce que D3.JS qui permet une modélisation innovante pour analyse de données, bon courage pour faire ça avec php !!!

[News ] Centres d’intérêts : Recherche opérationnelle . Mise a jour parfois

icone_news

Introduction


Suite à l’entrée en fin de ma formation Eicnam Cti (Reste 2 Ues et le mémoire, glups), je compte vraiment me recentrer sur la recherche opérationnelle (simplexe, graphes), et également surtout les prédicats, et la déduction naturelle qui sont vraiment des univers et des outils impressionants.

L’autre partie qui m’intéresse dans ma formation est l’ingénierie commerciale , mais moins que les deux premières parties.

NFP 108 est vraiment fournie, assez difficile(Les prédicats),  et très différente selon les régions, la version du Cnam Paris est vraiment complète sur les prédicats et excellente et ouvre la voie à des déductions inespérées, pour qui maitrise… La version de Bretagne est axée à fond sur les automates .Les professeurs NFP108 de Paris sont des sortes de génies. Lorsque l’on voit un logiciel comme COQ , on comprends que l’on puisse passer son temps à exercer sur pareil logiciel afin de prouver des problèmes possédant des tas d’aspects différents.

RCP 101 et RCP 110 : Mes livres pour enfin commencer à tenter de faire de l’optimisation concrete :

Programmation linéaire avec Excel 55 Problèmes d’optimisation pas à pas de EYROLLES: Mai 2018: Trop tooop , j’avance déjà et publierais un post sur toutes mes méthodes bientôt . Avec ce livre on peut commencer à traiter en mode technicien les problèmes concrets d’optimisation ; sans trop se soucier de la théorie . Le but étant d’avoir pas mal de tableaux Excel prêts pour les cas globaux et savoir Les exploiter avec le solveur. On peut déjà ainsi minimiser ou maximiser pas mal de cas !.

Mai 2018 : Springer Numeric optimisation de 1989 : un anglais facile à comprendre et le peu que j’ai lu c’est super structure et gradué dans l’apprentissage . J’hallucine carrement a fond comme c’est bien écris : on comprends bien c’est dénué de toute information ou théorie inutile!. J’ai aussi tout le reste de livres springer sur le simplexe . Ça me rappelle la clarté des livres Cisco mais encore en mieux puissance 100 , Il me semble que ca provient droit des usa ou peut être de UK .

Programmation linéaire (5 euros): Eric Jacquet :Trop sympa avec des exos Excel, accessible, et il parle des solveurs.

La programmation linéaire (6 euros): Bruno H Solnik : Accessible avec de bons rappels de maths, un exemple trop sympa de vendeur de chaussettes qui doit optimiser ses gains.

Précis de recherche opérationnel ‘Version des années 1965-1970 ‘ : Un flot d’informations trop fourni à mon goût. Une graduation de l’apprentissage du plus simple au plus complexe non présente à mon gout. Peut être la dernière version est mieux ?

La programmation linéaire appliquée à l’entreprise : DUNOD 1970 : Aux premiers abords  syntaxe sympa pour l’introduction : J’ai l’impression lors d’une première lecture , que le niveau dans les années 70 était plus haut que maintenant aux premiers abords. Il s’agissait d’une autre époque ou les idées étaient fondées et très profondes et complexes, basées sur de lourdes réflexions. Ni Internet, ni Excel n’existait, le niveau était extrême , et à l’époque la production était importante en France.

Il semble que je doive acheter https://www.eyrolles.com/Informatique/Livre/recherche-operationnelle-tome-1-9782729875091 mais c’est vraiment hyper cher !

Bref, dans tout cela , ce qui est le plus fascinant, c’est ‘ l’optimisation sous contraintes’ ainsi que la fonction à optimiser traduit l’objectif suivi par l’entreprise.’

 

Je compte faire RCP 110 mais pas de suite, vu qu’elle n’est pas obligatoire de suite, mais dans tous les cas je me dirige vers cela, et mon niveau actuel n’est pas assez bon le but étant de devenir opérationnel immédiatement sur les problèmes courants (horaires, objets à vendre, mélanges, flux par exemple), même si j’ai obtenu les UES, normal je faisais plein d’autres choses en même temps, de plus une fois l’UE obtenue, il faut continuer à la pratiquer pendant des années, selon moi , par contre je vais évoluer, et si je ne fais que ça pendant des années, ça va forcément avancer ) ?. Dans l’idée, il faudrait éventuellement ne plus faire que de la recherche opérationnelle, et presque plus de développement .
La démarche n’est pas du tout d’abandonner une UE que l’on a obtenu, mais il faut au contraire conserver ses classeurs et les relire si possible souvent.

Ce qui est drôle, c’est qu’à la suite de l’obtention du diplôme, j’aimerais prendre toutes les UES facultatives du cursus je pense, en fonction du temps .

[AngularJs + Php MYSQL ] Une table Html infinite scroll simple, sur base de donnée

angularjsphp_1_

Introduction


Avec Angularjs 1.6 , on ne télécharge pas tout un modèle de données lorsqu’il est très grand. Par exemple, pour, afficher toute une table de log de 150 MO, évidemment, on ne charge pas toute la table d’un seul coup dans la mémoire vive, ce serait bien trop couteux. Dans d’autres cas, dans le cadre d’une vue spéciale dans une application, on peut envisager de télécharger tout le modèle de données ( Par exemple, 500 utilisateurs)

L’idée de l’infinite Scroll sur base de donnée


L’idée étant d’afficher les 20 premiers enregistrements de la table, puis ensuite, lorsque l’on atteints le bas de la page, charger les 20 suivants, puis ainsi de suite, de cette manière, l’utilisateur a la sensation d’être en temps réel.

Le problème


Le problème vient lorsque l’on commence à utiliser des filtres dans le Back End, en effet, cela complique le système car les enregistrements arrivent alors dans le désordre, et nous verront cela dans une seconde partie.

Petit Rappel de l’architecture MVC de AngularJs 1.6 ( != Angular 5) :


file-page1.jpg

UNE IDEE DE LINFINITE SCROLL EN ACTION :

infitie.jpg

PHASE 1 Le code pour une table auto incrémentale, dont aucun enregistrement n’a jamais été supprimé

LA VUE HTML

On commence à créer la table dans la vue comme ceci :

<table class="table table-hover sttable table-responsive table-bordered ">
   <thead>
      <th>Id</th>
      <th>identifiant principal</th>
      <th>sessionId</th>
      <th>ip</th>
      <th>message</th>
      <th>timestamp</th>
      <th>url</th>
      <th>data</th>
      </tr>
   </thead>
   <tbody>
      <tr ng-repeat="row in logs track by $index">
         <td>{{::row.id}}</td>
         <td>{{::row.identifiantP}}</td>
         <td>{{::row.sessionId}}</td>
         <td>{{::row.ip}}</td>
         <td>{{::row.message}}</td>
         <td>{{::row.timestamp}}</td>
         <td>{{::row.url}}</td>
         <td>{{::row.data}}</td>
      </tr>
   </tbody>
</table>

Notes :

  • Remarquer qu’il faut ajouter ‘track by $index’ au ng-repeat pour que cela fonctionne.
  • Les :: sont utiles pour debinder afin de récupérer de la mémoire, en effet, pas besoin de 2 ways binding dans un simple reporting.

Ensuite , dans mon controleur ANGULARJS 1.6, je tape les instructions suivantes :

var startRowId = 0;
$scope.read_logs = function() {

    $http.post(wsUrl, {
            read_logs: JSON.stringify({
                'startRowId': startRowId
            })
        })
        .then(function(data) {

            if (startRowId == 0) {
                $scope.logs = data.data;
            }

            if (startRowId >= 20) {

                angular.forEach(data.data, function(obj) {
                    $scope.logs.push(obj);
                })

            }

        })

}

$scope.read_logs();

angular.element($window).bind("scroll", function() {
    var windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
    var body = document.body,
        html = document.documentElement;
    var docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
    windowBottom = windowHeight + window.pageYOffset;
    if (windowBottom >= docHeight) {
        startRowId += 20;
        $scope.read_logs();
    }
});

Explication :

  • startRowId est la valeur de la première ligne de notre tableau et dans la table SQL simultanément (C’est ‘synchro’).
  • $scope.read_logs() est la fonction qui lit et récupère en AJAX le tableau json habituel, si startRowId est = à 0 alors, il crée tout simplement les données dans la table HTML, par contre, si startRowId >= 20, alors il sait qu’il doit AJOUTER(push) au modèle de données les données qui arrivent, et qui sont forcément les 20 suivantes dans la table logs !.
  • angular.element($window).bind(« scroll », function() est la function qui détecte quand on arrive à la fin de la page, et exécute de nouveau $scope.read_logs() tout en incrémentant startRowId de 20 , de manière à dire au back end : ‘Lit moi les 20 suivants ‘
  • angular.forEach lui ajoute au tableau d’objet JSON $scope.logs les nouveaux objets récupérés.

Le Back End qui traite la requête SQL :

// Le web Service
if(!empty($_POST["read_logs"]) AND $_SESSION['auth']) {

	$json = $_POST["read_logs"];;
	$data = json_decode($json);
	echo json_encode($db->read_logs($data),JSON_NUMERIC_CHECK); // Renvoie le tableau JSON
} 

 //~ La fonction, normalement située dans une Classe PHP, je ne peux pas tout mettre ..
 public function read_logs($data) {
     $sql = " SELECT * FROM Log ";
     if ($data - > startRowId) {
         $sql. = " WHERE id >= ".$data - > startRowId;
     }

     $sql. = " LIMIT 20";

     $responseArray = array();
     if ($result = $this - > conn2 - > query($sql)) {
         while ($obj = $result - > fetch_object()) {
             array_push($responseArray, $obj);
         }
         mysqli_free_result($result); // Free result set
     }
     mysqli_close($this - > conn2);
     return $responseArray;
 }

Explication : Si le back end reçois la variable startRowId, alors il crée la requête SQL en conséquence, à chaque fois donc que l’on atteint la fin de la page…

Conclusion :

Voilà , on a un infinite scroll qui fonctionne seulement sur une table auto incrémentale et ou aucun enregistrement n’aura été supprimé.

Cependant, comment faire si l’on veut ajouter des filtres simultanés dans la requête SQL Back End , ou si on a supprimé des lignes (row) dans notre table SQL, du coup l’id auto increment ne respecte plus le n+1 ? Cela complique bien, puisque les ‘rows’ lignes n’arrivent pas forcément dans l’ordre…
C’est ce que nous allons analyser et voir dans la seconde partie.

PHASE 2 Le code pour une table auto incrémentale, dont des enregistrements ont été supprimés

ON voit que si l’on supprime 3 enregistrements au début de notre table SQL, par exemple eux qui ont respectivement les ids 3,4 et 5 , notre script de la phase 1 ne fonctionne plus correctement, et veut toujout nous afficher le slignes à partir de l’id 20 , puis de l’id 40 puis de l’id 60 etc …. Pourquoi ? et bien c’est à cause du ’20’ statique qui est ajouté à chaque fois dans le WHERE de la requête SQL et qui fausse alors notre résultat, si les 20 premiers résultats ne sont pas une suite en n+1 !

La solution est donc de passer l’id du dernier enregistrement acquis par le front endà notre requête SQL en lieu et place du chiffre statique (20 ou 40 ou 60 etc ..), afin qu’elle ne plante jamais ! Autrement dit, à chaque fois qu’une nouvelle page s’affiche, je récupère l’ID de la dernière ligne affichée, puis l’envoie au back end, qui lui, reconstitue la requête avec le dernièr ID obtenu (PAr exemple 23), puis renvoie les 20 prochaines données !

On modifie le code en conséquence :

Le controleur

var startRowId = 0;
$scope.read_logs = function() {

    $http.post(wsUrl, {
            read_logs: JSON.stringify({
                'startRowId': startRowId
            })
        })
        .then(function(data) {

            if (startRowId == 0) {
                $scope.logs = data.data;
            }

            if (startRowId != 0) {

                angular.forEach(data.data, function(obj) {
                    $scope.logs.push(obj);
                })

            }

        })

}

$scope.read_logs();

angular.element($window).bind("scroll", function() {
    var windowHeight = "innerHeight" in window ? window.innerHeight : document.documentElement.offsetHeight;
    var body = document.body,
        html = document.documentElement;
    var docHeight = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
    windowBottom = windowHeight + window.pageYOffset;
    if (windowBottom >= docHeight) {
       startRowId = $scope.hk_logs[$scope.hk_logs.length - 1].id;
       $scope.read_hk_logs();
    }
});

Explication : On récupère lorsqu’on a scrollé l’id de la dernière ligne affichée dans le tableau, afin de l’envoyer à la requête SQL

Phase 3 : Mais moi je veux utiliser des filtres aussi, quand je fais de l’infinite Scroll !

En fait, maintenant que l’on a résolu le problème de la dernière ligne (Qu’on aurait également pu traiter dans le back end directement), Ajouter des filtres est plutôt facile, il faut ajouter un select dans le front end, contenant les critères de filtres, puis, envoyer le critère au back end qui l’ajoute à la fin de la requête SQL, dans un WHERE :

Ma requête $http.post envoie désormais des filtres (dans le front end):


$http.post(wsUrl, {
read_logs: JSON.stringify({
'startRowId': startRowId,
'start_month_date': $scope.start_month_date,
'start_year_date': $scope.start_year_date,
'kitIdFilter': $scope.kitIdFilter,
'hkmessageFilter':$scope.hkmessageFilter,
'hkIpFilter':$scope.hkIpFilter
})
})

Puis, mon back end crée la requête SQL en conséquence :

 //~ Read LOGS
    public function read_logs($data)

    {
        $sql  = " SELECT * FROM Log WHERE 1=1";

		if($data->startRowId){

			$sql .= " AND id >= ".$data->startRowId ;
		} 

		if ($data->start_month_date) {

            $sql .= " AND MONTH(timestamp) = '" . $data->start_month_date . "'";
        }
        if ($data->start_year_date)  {

            $sql .= " AND YEAR(timestamp) = '" . $data->start_year_date . "'";
        }

		 if ($data->homekitIdFilter)  {

            $sql .= " AND kitId = '" . $data-><span 				data-mce-type="bookmark" 				id="mce_SELREST_start" 				data-mce-style="overflow:hidden;line-height:0" 				style="overflow:hidden;line-height:0" 			></span>kitIdFilter . "'";
        }

         if ($data->hkmessageFilter)  {

            $sql .= " AND message = '" . $data->hkmessageFilter . "'";
        }

        if ($data->hkIpFilter)  {

            $sql .= " AND ip = '" . $data->hkIpFilter . "'";
        }

		$sql.= " LIMIT 20";

        $responseArray = array();
        if ($result = $this->conn2->query($sql)) {
            while ($obj = $result->fetch_object()) {
                array_push($responseArray, $obj);
            }
            mysqli_free_result($result); // Free result set
        }
         mysqli_close($this->conn2);
        return $responseArray;
    }