vendredi 6 mars 2020

Début en Python

Compte tenu de la montée en puissance de Python en entreprise, je me devais d'en apprendre un peu plus sur le langage.
Il parait en effet que ce langage remplace même le VBA au niveau bureautique, mais aussi les SAS, voir R pour les calculs statistiques et financier.
Est-ce parce qu'il est appris par les universitaires ?
Je suis plutôt Ruby, mais il parait que Ruby s'est inspiré de Python.
Je vous partage ici mes notes rapides sans blabla. Pour une immersion un peu moins brutale, n'hésitez pas à vous référé à OpenClassRoom.

Alors Python est sensible au format. C'est à dire que les indentations obligatoires comptent dans la syntaxe du langage. Cela va être rude pour les personnes qui codait un script à l'arrache avec un langage hyper permissif.

  • Indentation obligatoire
  • Extension du langage : Python (.py)

Mes notes rapides :

#commentaire
print #afficher
annee = input("Saisissez une année : ") #récupération entrée
annee = int(annee) #cast en int

("chaîne de caractères")
('chaîne de caractères')
("""chaîne de caractères""")
"essai\nsur\nplusieurs\nlignes"

Opérateurs :
+=
%
==
!=
not

Partie entière d'une division
10 // 3

a,b = b,a # permutation

x = y = 3 #multi-affectation

 1 + 1 + \
 3 + 4 #\ permet de continuer l'instruction sur la ligne suivante

type(x) #type de classe de l'élément - objet

#indentation + ne pas oublier le ":"
if 0 == annee % 400 or (annee % 4 == 0 and annee % 100 != 0):
    print("L'année saisie est bissextile.")
else:
    print("L'année saisie n'est pas bissextile.")

if, elif et else #else if

if lettre in "AEIOUYaeiouy": # lettre est une voyelle

while i < 10:
i+=1

for element in sequence:

break (arrête la boucle), continue (revient au while)

def fonction(param1, max=10): #declaration fonction ; avec valeur par défaut

#fonction lambda "simple"
f = lambda x: x * x
>>> f(5)
25
lambda x, y: x + y

Module : ensemble de fonctions, scripts cohérents entre eux
help("math")
help("math.sqrt")

import module

import math as mathematiques #renommer l'espace de nom
mathematiques.sqrt(25)

#import d'une fonction et non pas le module complet
from math import fabs
from math import * #import de teoutes les fonctions
from nom_bibliotheque.objets import bouton #import que du package

#Exemple equivalent
from package.fonctions import table
table(5) # Appel de la fonction table

# Ou ...
import package.fonctions
package.fonctions.table(5)  # Appel de la fonction table

#Gestion des exceptions
try:
    # Test d'instruction(s)
except type_de_l_exception: #ex.: NameError, TypeError, ZeroDivisionError
    # Traitement en cas d'erreur
finally:
    # Instruction(s) exécutée(s) qu'il y ait eu des erreurs ou non

#Retourner l'erreur
try:
    # Bloc de test
except type_de_l_exception as exception_retournee:
    print("Voici l'erreur :", exception_retournee)

pass #mot clé pour ne rien faire; python attendant toujours une instruction

> Vocabulaire :

  • LIST : ["apple", "banana", "cherry"] (ordered, changeable, and allows duplicate members)
  • TUPLE : ("apple", "banana", "cherry")
  • SET : {"apple", "banana", "cherry"} (does not allow duplicate members)
  • DICTIONNARY : {"name": "apple", "color": "green"}

#slice premier élément%
x = ['a', 'b', 'c']
puts x[1:] # => b,c

jeudi 5 mars 2020

R'Quid

  • Le blog
Il existe de nombreux site dans le domaine du décisionnel. Ce site mets l'accent sur quelques astuces, sur l'importance des normes & standards, tente de faire prendre conscience de l'utilité des méthodes agiles.

L'objectif est de vulgariser certaines notions, décrire l'actualité des outils et acteurs du domaine du décisionnel (BI : Business Intelligence).

Ce site sert également de référence au projet R'QUID, qui a pour but de créer des outils, permettant un gain de temps sur les parties récurrentes ne représentant qu'un faible intérêt fonctionnel.
L'objectif étant de se concentrer sur la matière à forte valeur ajoutée, celle qui vous différencie des autres entreprises, voir de vos concurrents : votre métier !


  • Le projet

L'objectif de R'QUID peut se résumer en 2 phrases :
- La loi de Pareto 80/20
- Norme plutôt que du code (même philosophie que RoR)

- La loi de Pareto 80/20

80% des besoins sont génériques et 20% sont spécifiques. Enregistrer des données clients, traiter un processus, vérifier un email ou un numéro de téléphone. Le socle technique est toujours identique.
Ces 80% de besoins demandent 20% d'effort, voir moins, là est l'objectif d'R'QUID !
Surtout que 80% de ces besoins sont les mêmes d'une entreprise à l'autre !
Le reste qui est spécifique est souvent plus complexe. Il porte l'intérêt fonctionnel pour l'utilisateur final et est lié à l'activité du client.

- Norme plutôt que du code

Afin de faciliter le traitement des données, la mise en place de norme permet un gain de temps que l'on ne perçoit pas toujours. Nommer ses champs DATE en DAT_champ par exemple permettra d'appliquer un comportement standard plutôt qu'un comportement spécifique à chaque données date. Ce n'est peut-être pas très parlant car ici le type de données peut être un critère, mais on peut étendre ce principe à n'importe quel besoin.

Les normes sont aussi importantes pour la compréhension, la maintenance et la reprise de code. Il arrive que des sociétés n'aient pas de normes. Nous vous en proposons donc une !

Qu'est-ce qu'un besoin standard ?

Le chargement d'un DatawareHouse à partir d'un système opérationnel afin de produire des reportings sur différents axes d'analyse.

Les traitements sont les suivants :
- Charger les données
- Vérifier le format des données
- Vérifier le référentiel des données : warning, rejet, recyclage
- Transformer la donnée d'un référentiel vers un autre

Quel processus adopter ?

Modélisation & Méta-données :

Le plus important est la donnée. Il faut stocker et documenter la donnée dans un endroit unique.
Par exemple en la modélisant dans PowerAMC. Cet outil de modélisation de données permet également d'ajouter de la méta-données (meta-data) et permet donc d'ajouter des attributs à la données.
Par exemple : un type de donnée supplémentaire, une règle de gestion associée, une version...

Quelle utilité, pour quel gain ?

N'avoir qu'une seule source offre de nombreux avantages. On peut par exemple générer du code sur tout le processus.

Exemple :

- A partir du modèle PowerAMC on peut générer une documentation Word du modèle. Un bout de code, un template suffit à générer une doc à jour, comprenant les méta-données insérées (ex. version de la modification, un libellé, une classe, règle de gestion, source de la donnée, groupe utilisateurs...)

- Générer les jobs standards ETL (avec DataStage par exemple). Charger, vérifier le format, gérer les référentiels, le traitement des rejets...

- A partir du modèle on peut générer via du code de l'API Bo (Business Objects), l'ensemble des objets, classes, indicateurs. Dans le cas contraire la tâche est fastidieuse et les divergences entre modèle physique de données et modèle contenu dans l'outil sont régulièrement présents.

Cela permet de garder une cohérence globale de la donnée et de propager à très faible coût sa mise à jour.

AutoIt - Automatisation de tâche

Ruby permet de gérer un certain nombre de tâches sur les OLE windows (objet COM). Certaines technologies anciennes n'ont pas de dll (ex.: DDE).
Pour aller plus loin avec l'automatisation (automation) il existe des langages permettant d'émuler les clics souris ou les touches du clavier. AutoIt est un bon exemple, très poussé il permet de créer un exécutable et même de gérer une GUI (IHM).

Certaines options de logiciel ne sont accessibles que via l'interface graphique (ou bien connaître la bonne fonction de la dll à lancer...)
Ex.: export des méta-données de Datastage.
Il peut se révéler alors très pratique d'automatiser toute une chaine, export des méta-donneés inclues, ce qui n'est possible autrement et là... on gagne de la valeur ajoutée !
Rien n'est impossible en informatique, cela demande du temps et de la compétence.

AutoIt avec Ruby ? vous pensez bien que Ruby ne pouvait pas passer à côté d'une si belle librairie !
>$ gem install watir
enregistrer la dll (démarrez exécuter > "regsvr32 C:\ruby\lib\ruby\gems\1.8\gems\watir-1.4.1\watir\AutoItX3.dll")

Pour tester :
require 'rubygems'
require 'win32ole'
a = WIN32OLE.new("AutoItX3.Control")
a.mousemove(100, 100)
a.mouseclick

*-*-*-*-*-**-*-*-*-*-*-*-*
ruby with AutoItX

http://wtr.rubyforge.org/
http://www.autoitscript.com/forum/index.php?showtopic=57887&st=0&p=437832&#entry437832
http://arton.hp.infoseek.co.jp/index.html
http://www.autoitscript.com/autoit3/
http://phrogz.net/ProgrammingRuby/win32.html

=======
gem install watir
regsvr32 C:\ruby\lib\ruby\gems\1.8\gems\watir-1.4.1\watir\AutoItX3.dll
irb
require 'rubygems'
require 'win32ole'
a = WIN32OLE.new("AutoItX3.Control")
a.mousemove(100, 100)
a.mouseclick
=======

regsvr32 C:\ruby\lib\ruby\gems\1.8\gems\watir-1.4.1\watir\AutoItX3.dll
irb
require 'win32ole'
a = WIN32OLE.new("AutoItX3.Control")
a.ole_methods

http://actsasbuffoon.wordpress.com/2008/12/30/introduction-to-autoitx3/
http://rubywithwatir.blogspot.com/
http://rpgmakerxp.frbb.net/apprendre-le-ruby-f33/les-apis-windows-t557.htm
http://www.rubycentral.com/book/win32.html


#system system( aCmd [, args ]* ) -> true or false
#Executes aCmd in a subshell, return true/false A detailed error code $?. Kernel::exec on page 415.
system("echo *")
system("echo", "*")
OU
exec( command [, args ])
Quote spéciale "7"
puts `ls`
puts %x{ls}


require 'rubygems'
require 'win32ole'

begin
autoIt = WIN32OLE.new("AutoItX3.Control")
rescue
#marche pas : démarrez>exécuter... ou cmd ...
#register dll
path = "D:\\eclipse\\ruby\\lib\\ruby\\gems\\1.8\\gems\\watir-1.6.2\\lib\\watir\\"
#/s silent
result = system "regsvr32 /s #{path}AutoItX3.dll"
if !result
puts "(E) Vérifier le chemin de la dll AutoIt"
puts "(E) ou exécuter (Démarrez>Exécuter) taper \"regsvr32 /s AutoItX3.dll\""
end
autoIt = WIN32OLE.new("AutoItX3.Control")
end

autoIt.mousemove(100, 100)
autoIt.mouseclick

===
Il existe un module aussi très intéressant concernant le remplissage de formulaire web !
Par exemple pour automatiser l'envoi de vos superbes photo de vacances sur Picasa, vous connecter à votre boîte mail, ou remplir un formulaire pour créer un ticket d'anomalie...
>$ gem install mechanize

[Doc] [test]

VBA - Excel - Coller en texte

Parce que ça fait plus de 2 ans qu'on me dit que c'est trop compliqué de gérer le copier valeur depuis une source externe, j'ai pris 2h de mon temps pour me remettre au VBA après de longues années. Bah c'était pas si compliqué...

Il peut-être utile de forcer le coller dans une feuille Excel pour n'en coller que les valeurs sans autres caractéristiques (mise en forme, format de nombre etc.).

Ex.: Sur une feuille servant d'Interface utilisateur avec des contrôles de format (par Macro).
Sans la captation de l’événement "coller" Excel va également prendre en compte les formats ce qui peut être un inconvénient si vous avez spécifié le format et le contrôle associé de votre cellule (problème principalement sur les chiffres et dates).

Ici nous gérons le cas particulier ou la source de données n'est pas Excel, mais une source Externe telle qu'Oracle (On récupère le contenu du presse Papier).

Vous devez activer la librairie suivante (Il est possible de l'activer par Macro également)
'Tools -> References -> Microsoft Forms 2.0 Object Library"

Dans l'éditeur de Macro (alt+F11), dans la feuille *ThisWorkbook* vous devez copier le code suivant :
'-- Prise en compte de la macro à l'ouverture du classeur
'-- L'action se réalise avec Ctrl+v et Ctrl+V (minuscule, majuscule)

Private Sub Workbook_Open()
    Application.CutCopyMode = False ' Rend le presse-papier disponible et vide

    Application.OnKey "^V", "PasteProc"
    Application.OnKey "^v", "PasteProc"
End Sub

'--Créer un module
Option Explicit

Private module
Public Clipboard As New MSForms.DataObject

'--Coller les procédures qui font le job

'--Il y a un respect du standard des tableaux prenant le caractère tabulation comme séparateur de colonne
'--Il ne reste donc plus qu'à tester si j'ai une source Excel ou Presse Papier
Public Sub PasteProc()
    Select Case Application.CutCopyMode
        Case Is = False
            Call PasteExtern
        Case Is = xlCopy
            Call PasteExcel
        Case Is = xlCut
            Call PasteExcel
    End Select
End Sub

Private Sub writeExcelFromClipboard()
'Tools -> References -> Microsoft Forms 2.0 Object Library
'of you will get a "Compile error: user-defined type not defined"
  Dim Clipboard As New MSForms.DataObject
  Dim S As String
  Dim i, j As Double

  Clipboard.GetFromClipboard
    S = Clipboard.GetText

    Dim start_r, start_c
    Dim tab_line() As String
    Dim tab_tab() As String
    ReDim tab_line(UBound(Split(S, vbCrLf)))

    'start_r = ActiveCell.Row '.Address
    'start_c = ActiveCell.Column
   
    tab_line = Split(S, vbCrLf)
    For i = 0 To UBound(tab_line)
      ReDim tab_tab(UBound(Split(tab_line(i), vbTab)))
      tab_tab = Split(tab_line(i), vbTab)
      For j = 0 To UBound(tab_tab)
        ActiveCell.NumberFormat = "@"
        'ActiveCell.Offset(start_r + i, start_c + j).Value = tab_tab(j)
        ActiveCell.Offset(i, j).Value = tab_tab(j)
      Next j
    Next i

  'readClipboard = S
End Sub

Private Sub PasteExcel()
  'Selection.NumberFormat = "@"
  'ActiveSheet.Paste
  Selection.PasteSpecial Paste:=xlPasteValues
End Sub

Private Sub PasteExtern()
  'ActiveCell.Value2 = readClipBoard 'Clipboard.GetFromClipboard
  Call writeExcelFromClipboard
  Clipboard.Clear
End Sub

mardi 4 février 2020

SQL Parser (Parseur de SQL) - Exemple Node JS

Après avoir essuyé quelques problèmes avec la librairie Ruby, j'ai décidé de me familiariser avec Node JS avec un exemple. Alors pourquoi pas avec le Parser Sql.

Quel est l'objectif d'un parseur SQL ?
L'objectif est simple. A partir des différentes requêtes, il est parfois utile de récupérer les différents éléments constituants les requêtes. Par exemple dans le cas d'un audit, savoir quels sont les données qui sont utilisées. On récupérera alors l'ensemble des champs de l'ensemble des requêtes.

Voici donc un exemple d'utilisation de la librairie. Avoir la liste des tables (la table et non l'alias) et des champs. Lien du package node-sql-parser.

//lire le fichier SQL
const DEBUG = true;
const fs = require('fs')
const path = '/'
const file = path + 'query.sql'

//récupérer le contenu du fichier dans une variable
if (fs.existsSync(file)) {
  var sql = fs.readFileSync(file).toString()//.split("\r\n");
}


//Parser le Sql
const opt = {
  database: 'MySQL' // MySQL is the default database
}
const { Parser } = require('node-sql-parser');
const parser = new Parser();

// {type}::{dbName}::{tableName} // type could be select, update, delete or insert
const { tableList, columnList, ast } = parser.parse(sql, opt);

//On ne s'intéresse qu'aux instructions select
//select::db::table => db.table
console.log('>> Tables :');
tableList.forEach(function(it, id) {
    if ('select::' == it.substring(0,8)) {
        x = it.substring(8).replace('::', '.');
        // console.log(x);
        tableList[id] = x;
    } else {
        tableList.splice(id, 1); //on enlève l'élément du tableau si ce n'est pas un select
    }
});

//select::table::champ => table.champ
console.log('\n>> Champs :');
columnList.forEach(function(it, id) {
    if ('select::' == it.substring(0,8)) {
        x = it.substring(8).replace('::', '.');
        // console.log(x);
        columnList[id] = x;
    } else {
        columnList.splice(id, 1);
    }
});

//Tri du tableau et valeur unique
tableList1 = tableList.toString().split(',').sort();
tableList1 = Array.from(new Set(tableList1)) //uniq
columnList1 = columnList.toString().split(',').sort();
columnList1 = Array.from(new Set(columnList1)) //uniq

//Affichage des tables et champs du fichier de requêtes
console.log(tableList1);
console.log(tableList1.length);
console.log(columnList1);
console.log(columnList1.length);

Remarque :
Je n'ai pas réussi à intégrer à mon script d'autres types d'instruction (en dehors de select). Cela produit une erreur.
Ce traitement ne gère pas le cas où l'on ne définit pas explicitement la table pour le champ, ce que permet SQL tant qu'il n'y a pas de nom identique.