{"meta":{"title":"Rendu des données sous forme de graphes","intro":"Découvrez comment visualiser les langages de programmation à partir de votre référentiel à l’aide de la bibliothèque D3.js et Ruby Octokit.","product":"API REST","breadcrumbs":[{"href":"/fr/rest","title":"API REST"},{"href":"/fr/rest/guides","title":"Guides"},{"href":"/fr/rest/guides/rendering-data-as-graphs","title":"Rendu des données sous forme de graphes"}],"documentType":"article"},"body":"# Rendu des données sous forme de graphes\n\nDécouvrez comment visualiser les langages de programmation à partir de votre référentiel à l’aide de la bibliothèque D3.js et Ruby Octokit.\n\nDans ce guide, nous allons utiliser l’API pour récupérer des informations sur les dépôts dont nous sommes propriétaires et sur les langages de programmation qu’ils utilisent. Ensuite, nous allons visualiser ces informations de deux façons différentes à l’aide de la bibliothèque [D3.js](https://d3js.org/). Pour interagir avec l’API GitHub, nous utiliserons l’excellente bibliothèque Ruby, [Octokit](https://github.com/octokit/octokit.rb).\n\nSi vous ne l’avez pas déjà fait, vous devez lire le guide [Principes de base de l’authentification](/fr/apps/oauth-apps/building-oauth-apps/authenticating-to-the-rest-api-with-an-oauth-app) avant d’utiliser cet exemple. Vous trouverez le code source complet de ce projet dans le dépôt [platform-samples](https://github.com/github/platform-samples/tree/master/api/ruby/rendering-data-as-graphs).\n\nCommençons !\n\n## Configurer une OAuth app\n\nTout d’abord, [inscrivez une nouvelle application](https://github.com/settings/applications/new) dans GitHub. Définissez les URL principales et les URL de rappel sur `http://localhost:4567/`. Comme [précédemment](/fr/apps/oauth-apps/building-oauth-apps/authenticating-to-the-rest-api-with-an-oauth-app), nous allons gérer l’authentification pour l’API en implémentant un intergiciel Rack à l’aide de [sinatra-auth-github](https://rubygems.org/gems/sinatra_auth_github) :\n\n```ruby\nrequire 'sinatra/auth/github'\n\nmodule Example\n  class MyGraphApp < Sinatra::Base\n    # !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!\n    # Instead, set and test environment variables, like below\n    # if ENV['GITHUB_CLIENT_ID'] && ENV['GITHUB_CLIENT_SECRET']\n    #  CLIENT_ID        = ENV['GITHUB_CLIENT_ID']\n    #  CLIENT_SECRET    = ENV['GITHUB_CLIENT_SECRET']\n    # end\n\n    CLIENT_ID = ENV['GH_GRAPH_CLIENT_ID']\n    CLIENT_SECRET = ENV['GH_GRAPH_SECRET_ID']\n\n    enable :sessions\n\n    set :github_options, {\n      :scopes    => \"repo\",\n      :secret    => CLIENT_SECRET,\n      :client_id => CLIENT_ID,\n      :callback_url => \"/\"\n    }\n\n    register Sinatra::Auth::Github\n\n    get '/' do\n      if !authenticated?\n        authenticate!\n      else\n        access_token = github_user[\"token\"]\n      end\n    end\n  end\nend\n```\n\nConfigurez un fichier *config.ru* similaire à celui de l’exemple précédent :\n\n```ruby\nENV['RACK_ENV'] ||= 'development'\nrequire \"rubygems\"\nrequire \"bundler/setup\"\n\nrequire File.expand_path(File.join(File.dirname(__FILE__), 'server'))\n\nrun Example::MyGraphApp\n```\n\n## Récupération des informations sur les dépôts\n\nCette fois-ci, pour parler à l’API GitHub, nous allons utiliser la [bibliothèque Octokit Ruby](https://github.com/octokit/octokit.rb). Cela est beaucoup plus facile que d’effectuer directement plusieurs appels REST. De plus, Octokit a été développée par un GitHubber et sa maintenance est régulièrement effectuée. Vous pouvez donc être sûr qu’elle fonctionnera.\n\nL’authentification auprès de l’API via Octokit est facile. Il vous suffit de passer votre identifiant de connexion et votre jeton au constructeur `Octokit::Client` :\n\n```ruby\nif !authenticated?\n  authenticate!\nelse\n  octokit_client = Octokit::Client.new(:login => github_user.login, :oauth_token => github_user.token)\nend\n```\n\nNous allons faire quelque chose d’intéressant avec les données concernant nos dépôts. Nous allons voir les différents langages de programmation qu’ils utilisent et compter ceux qui sont utilisés le plus souvent. Pour ce faire, nous devons d’abord obtenir la liste de nos dépôts auprès de l’API.\nAvec Octokit, cela ressemble à ceci :\n\n```ruby\nrepos = client.repositories\n```\n\nEnsuite, nous allons itérer sur chaque dépôt et compter le langage que GitHub lui associe :\n\n```ruby\nlanguage_obj = {}\nrepos.each do |repo|\n  # sometimes language can be nil\n  if repo.language\n    if !language_obj[repo.language]\n      language_obj[repo.language] = 1\n    else\n      language_obj[repo.language] += 1\n    end\n  end\nend\n\nlanguages.to_s\n```\n\nLorsque vous redémarrez votre serveur, votre page web doit afficher quelque chose qui ressemble à ceci :\n\n```ruby\n{\"JavaScript\"=>13, \"PHP\"=>1, \"Perl\"=>1, \"CoffeeScript\"=>2, \"Python\"=>1, \"Java\"=>3, \"Ruby\"=>3, \"Go\"=>1, \"C++\"=>1}\n```\n\nJusque-là, tout va bien, mais ce n'est pas très convivial. Une visualisation serait idéale pour nous aider à comprendre comment ces langages sont distribués. Nous allons ajouter ces comptes de langages dans D3 pour obtenir un graphique à barres représentant la popularité de chaque langage utilisé.\n\n## Visualisation des dénombrements de langues\n\nD3.js (ou plus simplement « D3 ») est une bibliothèque complète qui permet de créer de nombreux types de graphiques, de graphes et de visualisations interactives.\nNous n’allons pas aborder l’utilisation de D3 en détail dans ce guide. Cependant, pour une bonne introduction, nous vous conseillons de lire [D3 for Mortals](http://recursion.org/d3-for-mere-mortals/).\n\nD3 est une bibliothèque JavaScript qui est particulièrement bien adaptée à l’utilisation de données sous forme de tableaux. Nous allons donc convertir notre hachage Ruby en tableau JSON afin que JavaScript puisse l’utiliser dans le navigateur.\n\n```ruby\nlanguages = []\nlanguage_obj.each do |lang, count|\n  languages.push :language => lang, :count => count\nend\n\nerb :lang_freq, :locals => { :languages => languages.to_json}\n```\n\nNous allons simplement itérer sur chaque paire clé-valeur de notre objet et les ajouter dans un nouveau tableau. La raison pour laquelle nous n’avons pas fait cela plus tôt est que nous ne voulions pas itérer sur notre objet `language_obj` pendant sa création.\n\nMaintenant, *lang\\_freq.erb* va avoir besoin de JavaScript pour prendre en charge le rendu d’un graphique à barres.\nPour l’instant, vous pouvez simplement utiliser le code fourni ici et vous référer aux ressources ci-dessus pour en savoir plus sur le fonctionnement de D3 :\n\n```html\n<!DOCTYPE html>\n<meta charset=\"utf-8\">\n<html>\n  <head>\n    <script src=\"//cdnjs.cloudflare.com/ajax/libs/d3/3.0.1/d3.v3.min.js\"></script>\n    <style>\n    svg {\n      padding: 20px;\n    }\n    rect {\n      fill: #2d578b\n    }\n    text {\n      fill: white;\n    }\n    text.yAxis {\n      font-size: 12px;\n      font-family: Helvetica, sans-serif;\n      fill: black;\n    }\n    </style>\n  </head>\n  <body>\n    <p>Check this sweet data out:</p>\n    <div id=\"lang_freq\"></div>\n\n  </body>\n  <script>\n    var data = <%= languages %>;\n\n    var barWidth = 40;\n    var width = (barWidth + 10) * data.length;\n    var height = 300;\n\n    var x = d3.scale.linear().domain([0, data.length]).range([0, width]);\n    var y = d3.scale.linear().domain([0, d3.max(data, function(datum) { return datum.count; })]).\n      rangeRound([0, height]);\n\n    // add the canvas to the DOM\n    var languageBars = d3.select(\"#lang_freq\").\n      append(\"svg:svg\").\n      attr(\"width\", width).\n      attr(\"height\", height);\n\n    languageBars.selectAll(\"rect\").\n      data(data).\n      enter().\n      append(\"svg:rect\").\n      attr(\"x\", function(datum, index) { return x(index); }).\n      attr(\"y\", function(datum) { return height - y(datum.count); }).\n      attr(\"height\", function(datum) { return y(datum.count); }).\n      attr(\"width\", barWidth);\n\n    languageBars.selectAll(\"text\").\n      data(data).\n      enter().\n      append(\"svg:text\").\n      attr(\"x\", function(datum, index) { return x(index) + barWidth; }).\n      attr(\"y\", function(datum) { return height - y(datum.count); }).\n      attr(\"dx\", -barWidth/2).\n      attr(\"dy\", \"1.2em\").\n      attr(\"text-anchor\", \"middle\").\n      text(function(datum) { return datum.count;});\n\n    languageBars.selectAll(\"text.yAxis\").\n      data(data).\n      enter().append(\"svg:text\").\n      attr(\"x\", function(datum, index) { return x(index) + barWidth; }).\n      attr(\"y\", height).\n      attr(\"dx\", -barWidth/2).\n      attr(\"text-anchor\", \"middle\").\n      text(function(datum) { return datum.language;}).\n      attr(\"transform\", \"translate(0, 18)\").\n      attr(\"class\", \"yAxis\");\n  </script>\n</html>\n```\n\nOuf ! Encore une fois, vous n’avez pas à vous soucier de ce que fait la plus grande partie de ce code. La partie qui nous intéresse ici est une ligne située en haut (`var data = <%= languages %>;`) qui indique que nous passons notre tableau `languages` dans ERB afin de le manipuler.\n\nComme le suggère le guide « D3 for Mortals », ce n’est pas nécessairement la meilleure façon d’utiliser D3. Toutefois, cela sert à montrer comment utiliser la bibliothèque, ainsi que Octokit, pour réaliser des choses vraiment étonnantes.\n\n## Combinaison de différents appels d’API\n\nMaintenant, nous devons vous dire quelque chose : l’attribut `language` qui se trouve dans les dépôts identifie uniquement le langage « principal » défini. Cela signifie que si vous disposez d’un dépôt qui combine plusieurs langages, celui avec le plus d’octets de code sera considéré comme le langage principal.\n\nNous allons combiner quelques appels d’API pour obtenir une *vraie* représentation du langage qui a le plus grand nombre d’octets écrits dans l’ensemble du code. Le [compartimentage](https://www.d3-graph-gallery.com/treemap.html) est un excellent moyen de visualiser la taille des langages de codage utilisés, plutôt que leur nombre. Nous devons construire un tableau d’objets qui ressemble à ceci :\n\n```json\n[ { \"name\": \"language1\", \"size\": 100},\n  { \"name\": \"language2\", \"size\": 23}\n  ...\n]\n```\n\nÉtant donné que nous avons déjà la liste des dépôts ci-dessus, examinons chacun d’eux avant d'appeler le [point de terminaison GET /repos/{owner}/{repo}/languages](/fr/rest/repos/repos#list-repository-languages) :\n\n```ruby\nrepos.each do |repo|\n  repo_name = repo.name\n  repo_langs = octokit_client.languages(\"#{github_user.login}/#{repo_name}\")\nend\n```\n\nÀ présent, nous allons ajouter cumulativement chaque langage trouvé à une liste de langages :\n\n```ruby\nrepo_langs.each do |lang, count|\n  if !language_obj[lang]\n    language_obj[lang] = count\n  else\n    language_obj[lang] += count\n  end\nend\n```\n\nAprès cela, nous allons mettre en forme le contenu dans une structure que D3 comprend :\n\n```ruby\nlanguage_obj.each do |lang, count|\n  language_byte_count.push :name => \"#{lang} (#{count})\", :count => count\nend\n\n# some mandatory formatting for D3\nlanguage_bytes = [ :name => \"language_bytes\", :elements => language_byte_count]\n```\n\n(Pour plus d’informations sur la magie des cartes arborescentes D3, consultez [ce tutoriel simple](/fr/rest/repos/repos#list-repository-languages).)\n\nPour terminer, nous allons passer ces informations JSON au même modèle ERB :\n\n```ruby\nerb :lang_freq, :locals => { :languages => languages.to_json, :language_byte_count => language_bytes.to_json}\n```\n\nComme tout à l’heure, voici un ensemble de JavaScript que vous pouvez ajouter directement dans votre modèle :\n\n```html\n<div id=\"byte_freq\"></div>\n<script>\n  var language_bytes = <%= language_byte_count %>\n  var childrenFunction = function(d){return d.elements};\n  var sizeFunction = function(d){return d.count;};\n  var colorFunction = function(d){return Math.floor(Math.random()*20)};\n  var nameFunction = function(d){return d.name;};\n\n  var color = d3.scale.linear()\n              .domain([0,10,15,20])\n              .range([\"grey\",\"green\",\"yellow\",\"red\"]);\n\n  drawTreemap(5000, 2000, '#byte_freq', language_bytes, childrenFunction, nameFunction, sizeFunction, colorFunction, color);\n\n  function drawTreemap(height,width,elementSelector,language_bytes,childrenFunction,nameFunction,sizeFunction,colorFunction,colorScale){\n\n      var treemap = d3.layout.treemap()\n          .children(childrenFunction)\n          .size([width,height])\n          .value(sizeFunction);\n\n      var div = d3.select(elementSelector)\n          .append(\"div\")\n          .style(\"position\",\"relative\")\n          .style(\"width\",width + \"px\")\n          .style(\"height\",height + \"px\");\n\n      div.data(language_bytes).selectAll(\"div\")\n          .data(function(d){return treemap.nodes(d);})\n          .enter()\n          .append(\"div\")\n          .attr(\"class\",\"cell\")\n          .style(\"background\",function(d){ return colorScale(colorFunction(d));})\n          .call(cell)\n          .text(nameFunction);\n  }\n\n  function cell(){\n      this\n          .style(\"left\",function(d){return d.x + \"px\";})\n          .style(\"top\",function(d){return d.y + \"px\";})\n          .style(\"width\",function(d){return d.dx - 1 + \"px\";})\n          .style(\"height\",function(d){return d.dy - 1 + \"px\";});\n  }\n</script>\n```\n\nEt voilà ! De beaux rectangles contenant les langages de vos dépôts, avec des proportions relatives qui sont compréhensibles en un coup d’œil. Pour que toutes les informations s’affichent correctement, vous devrez peut-être ajuster la hauteur et la largeur de votre carte arborescente, passées comme les deux premiers arguments à `drawTreemap` ci-dessus."}