Affichage des articles dont le libellé est vba. Afficher tous les articles
Affichage des articles dont le libellé est vba. Afficher tous les articles

vendredi 15 janvier 2016

Automatiser le remplissage de vos powerpoints

Pour continuer sur la lignée du Je pleure sur le boulot chiant voici un petit article pour raconter comment arrêter de le faire =)
Mon cas d'usage s'écrit sous forme de User Story de la manière suivante :
En tant que responsable / chef de projet / manager / contrôleur de gestion / ...
Je dois remplir des fichiers powerpoint standards avec des données venant d'un fichier excel standard
Pour présenter le rendu à mon chef / patron / client / partenaire / etc.
 Mon patron me dit souvent qu'il faut distinguer entre trois catégories de personnes :
  • Les Fainéants, c'est à dire ceux qui ne font rien,
  • Les Feignants, engeance encore pire car ils font semblant de faire,
  • Les Paresseux, objectif et graal de tout bon informaticien, c'est à dire ceux qui ne veulent jamais faire deux fois la même chose peu intéressante.

Et je suis un paresseux !

Dans ma vie de PMO, j'ai eu et je continue de remplir des fichiers powerpoints peu intéressants, en récupérant des informations d'un fichier excel qui sert de modèle. Je suis persuadé que je suis le seul à faire cela, quoiqu'en en discutant avec des collègues, amis et autres, il semblerait que ce ne soit pas le cas =)

D'où, une réflexion : pourquoi ne pas se débrouiller pour arrêter de faire des copier/coller, ne décrire qu'une seule fois la manière de remplir un fichier, puis d'automatiser le remplissage.
Si Microsoft avait été intelligent, la firme de Redmond aurait prévu le principe du publipostage avec powerpoint. Mais ce n'est pas le cas >_<

Arrêtons le teasing, voici le monde réel et une petit journée de travail que vous trouverez ici pour automatiser le remplissage de vos PPT !

Comment ça marche ? 

Le principe est relativement simple :
  • Récupérer les fichiers sur le github
  • Ouvrir votre fichier PPT et activer les macros
  • Entrer dans le mode macro, importer les fichiers
  • Ajouter les modules manquants
  • Compléter votre PPT avec un format à la moustache
  • Enregistrer votre fichier au format pptm ou potm
  • Lancer la macro "populateMyPPT"
Il faut bien sûr activer les macro pour que cela fonctionne, et avoir des fichiers excel standard en entrée.

Des exemples sont à venir sur le git pour montrer le fonctionnement.

Et la suite

Nous sommes le 15 janvier, j'ai ouvert le projet le 13, et pour le moment la machine permet uniquement d'automatiser l'insertion de texte venant d'un seul classeur excel.
L'objectif est de parvenir à récupérer des plages, des images, etc. Et ça devrait arriver vite.

Si cela vous intéresse, la feuille de route est sous trello.

jeudi 3 décembre 2015

Optimisation VBA-Excel sur des boucles et du remplacement de données

Le document sur lequel nous travaillons est présent sur github.

Cas d'usage

Dans mes activités de PMO (personne n'est parfait) je m'amuse (!) à construire de jolies feuilles de route, ou roadmap pour les intimes du barratin globish. Les meilleurs amis du PMO pour ce type d'activité sont Excel et powerpoint :
  • Excel pour la collecte de données (je suis un IT guy quand même),
  • Powerpoint pour la couche de présentation un peu "glam'".
J'en connais qui n'utilise que l'un ou l'autre. Je préfère les couplages intelligents.
Bon, pour finir l'explication, j'ai donc une très belle feuille de route, avec beaucoup de jalons, sous format excel, un nombre de clients et de domaines applicatifs importants, et un besoin de présenter des visions différenciées à chacun.
Pour cela, rien ne vaut Excel (toujours), beaucoup de mise en forme conditionnelle et quelques macros.

Le problème

Le principe est de récupérer (à la main) une copie de la feuille de route pour la mettre dans un onglet complémentaire, puis de nettoyer les cases sans contenu visible pour que l'affichage soit un peu sympa.
Je partage ce petit bout de code avec vous, qui est la version 0.


Sub makeItClear()

Dim ws As Worksheet, r As Range, c As Range, nbligne As Long
Application.ScreenUpdating = False

Set ws = ThisWorkbook.Sheets("Feuil1")

nbligne = InputBox("nb ligne à traiter", , 20) + 0
Set r = ws.Range("d8:aa" & nbligne)

For Each c In r.Cells
   If c.Text = "" Then c.ClearContents
   If Len(c.Text) > 2 Then c.HorizontalAlignment = xlLeft
Next

Application.ScreenUpdating = True

End Sub


De manière très simple, l'algorithme marche, par contre, c'est long. Pour une plage d'une quarantaine de lignes, il faut compter entre quinze et vingt minutes. Pour la feuille de route complète, pas loin d'une heure.

Le pourquoi

Après beaucoup de recherche, il s'avère que la partie lourde de l'algorithme est la partie écriture/réécriture du classeur, c'est à dire :

   If c.Text = "" Then c.ClearContents

Le problème vient du coût d'accès systématique au classeur pour écrire. Rien de nouveau me direz vous, et vous avez parfaitement raison. Le coût d'accès en écriture est toujours ce qui coûte cher en I/O et casse les performance.

Un peu de craft

Le problème étant l'accès, la première idée qui me vient est donc de ne plus faire un accès simultanée mais un accès par "small batch".


Sub makeItClear()

Dim myCells As String
Dim ws As Worksheet, r As Range, c As Range, nbligne As Long

myCells = "$E$5" 'cellule d'init ou il n'y a rien
Application.ScreenUpdating = False

Set ws = ThisWorkbook.Sheets("Feuil1")

nbligne = InputBox("nb ligne à traiter", , 20) + 0
Set r = ws.Range("f8:ag" & nbligne)



For Each c In r.Cells
  If c = "" Then myCells = myCells + "," + c.Address
  If Len(c.Text) = 1 Then c.HorizontalAlignment = xlCenter
Next

ws.range(mycells).clearcontents

Application.ScreenUpdating = True

End Sub



Pour faire simple, au lieu de faire un accès immédiat, le principe est de stocker la liste de tous les cas positifs et de faire une suppression globale.
L'idée est belle, mais ça ne marche pas du premier coup ! Sur le fond, le soucis est que la plage est trop longue !

Plus de craft !

Alors un peu de tests, il semble que la limite du 'range' de VBA est de 256 caractères. Pourquoi ? Je ne sais pas, dans tous les cas, une chaine de 256 caractères passe le test, une chaine de 512 non.
Pour s'en sortir, il reste donc à éclater la chaine en sous-chaine de maximum 256 caractères. Dans tous les autres langages, j'aurais joué avec des tableaux. En VBA, c'est vraiment pas pratique. Il reste donc à jouer avec des chaines :-/
On va donc faire une modification dans le code et rajouter deux fonctions complémentaires :

Sub makeItClear()

Dim myCells As String, calculMode As Variant
...

myCells = Replace(myCells, "$", "")

While Len(myCells) > 0
  m = cutLongList(myCells, ",", 256)
  ws.Range(m).Value = ""
  myCells = Mid(myCells, Len(m) + 2)
Wend

Application.ScreenUpdating = True

End Sub



Je nettoie donc la chaine de plage des '$' pour réduire sa taille, puis je travaille une boucle de réduction de chaine.
Pour cela, il faut deux petites fonctions :


Private Function cutLongList(ByVal myString As String, ByVal delimiter As String, ByVal maxValue As Long) As String
Dim i As Long
cutLongList = myString

If Len(myString) < maxValue Then Exit Function 

  i = 0 
While isMyNumCarMyCar(myString, maxValue - i, delimiter) = False 
  i = i + 1
Wend 
cutLongList = Mid(myString, 1, maxValue - i - 1)
End Function 

Function isMyNumCarMyCar(ByVal myString As String, ByVal numCar As Long, ByVal myCar As String) As Boolean 
'''Pour vérifier qu'un numéro de caractères correspond à un caractère attendu
  isMyNumCarMyCar = IIf(Mid(myString, numCar, 1) = myCar, True, False) 
End Function

N'ayant pas de meilleures fonctionnalités pour faire du slicing de chaines avec des Arrays, la fonction 'isMyNumCarMyCar' permet de vérifier qu'un numéro de caractère de chaine est bien un caractère passé en paramètre.
Et 'cutLongList' permet juste de faire le retour de substring. Elle serait sans doute à améliorer avec quelques traitements d'erreurs.

Conclusion

Première conclusion, ce changement de code permet de passer d'un temps de traitement de 10 minutes à moins de 30 secondes. Evidemment, ça jette.
Au milieu, je retiens :
  • Que l'accès en écriture dans les classeurs excel est à limiter dans les boucles,
  • Que l'objet range de VBA contient au maximum 256 caractères,
  • Que les objets Arrays de VBA sont vraiment pas top en termes de capacité.
Pour la suite, on attaque les gros projets avec du templating de powerpoint avec une source excel.