{"meta":{"title":"Representar dados como gráficos","intro":"Aprenda a visualizar as linguagens de programação do seu repositório usando a biblioteca D3.js e o Ruby Octokit.","product":"API REST","breadcrumbs":[{"href":"/pt/rest","title":"API REST"},{"href":"/pt/rest/guides","title":"Guias"},{"href":"/pt/rest/guides/rendering-data-as-graphs","title":"Representar dados como gráficos"}],"documentType":"article"},"body":"# Representar dados como gráficos\n\nAprenda a visualizar as linguagens de programação do seu repositório usando a biblioteca D3.js e o Ruby Octokit.\n\nNeste guia, vamos usar a API para buscar informações sobre os repositórios dos quais somos proprietários e as linguagens de programação que os compõem. Em seguida, visualizaremos essas informações de algumas maneiras diferentes usando a biblioteca [D3.js](https://d3js.org/). Para interagir com a API do GitHub, você usará a biblioteca do Ruby, a [Octokit](https://github.com/octokit/octokit.rb), que é excelente.\n\nCaso ainda não tenha feito isso, leia o guia [Noções básicas de autenticação](/pt/apps/oauth-apps/building-oauth-apps/authenticating-to-the-rest-api-with-an-oauth-app) antes de iniciar este exemplo. Encontre o código-fonte completo deste projeto no repositório [platform-samples](https://github.com/github/platform-samples/tree/master/api/ruby/rendering-data-as-graphs).\n\nVamos começar imediatamente!\n\n## Configurar um OAuth app\n\nPrimeiro, [registre um novo aplicativo](https://github.com/settings/applications/new) no GitHub. Defina a URL principal e a URL de callback como `http://localhost:4567/`. Como [antes](/pt/apps/oauth-apps/building-oauth-apps/authenticating-to-the-rest-api-with-an-oauth-app), trataremos a autenticação da API implementando um middleware de rack usando [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\nConfigure um arquivo *config.ru* semelhante ao exemplo anterior:\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## Buscar informações do repositório\n\nDesta vez, para se comunicar com a API do GitHub, vamos usar a [biblioteca Octokit para Ruby](https://github.com/octokit/octokit.rb). Isso é muito mais fácil do que fazer diretamente um monte de chamadas REST. Além disso, o Octokit foi desenvolvido por um usuário do GitHub e é mantido ativamente, ou seja, você sabe que ele vai funcionar.\n\nÉ fácil a autenticação com a API através do Octokit. Basta transmitir seu logon e o token para o construtor `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\nVamos fazer algo interessante com os dados sobre nossos repositórios. Vamos ver as diferentes linguagens de programação que eles usam e contar quais são usadas com mais frequência. Para fazer isso, primeiro precisaremos de uma lista dos nossos repositórios na API.\nCom o Octokit, será algo parecido com isso:\n\n```ruby\nrepos = client.repositories\n```\n\nEm seguida, iteraremos sobre cada repositório e contaremos as linguagens que o GitHub associa a cada um deles.\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\nQuando você reiniciar seu servidor, a página da Web exibirá algo parecido com isto:\n\n```ruby\n{\"JavaScript\"=>13, \"PHP\"=>1, \"Perl\"=>1, \"CoffeeScript\"=>2, \"Python\"=>1, \"Java\"=>3, \"Ruby\"=>3, \"Go\"=>1, \"C++\"=>1}\n```\n\nAté agora, tudo bem, mas não é muito amigável para o usuário. Uma visualização será excelente para nos ajudar a entender como as contagens de linguagens são distribuídas. Vamos alimentar nossas contagens no D3 para obter um excelente gráfico de barras que representa a popularidade das linguagens que usamos.\n\n## Visualizar contagens de línguas\n\nD3.js, ou apenas D3, é uma biblioteca abrangente para criar muitos tipos de gráficos, gráficos e visualizações interativas.\nO uso detalhado do D3 não está no escopo deste guia, mas para ver um bom artigo introdutório, confira [D3 para mortais](http://recursion.org/d3-for-mere-mortals/).\n\nD3 é uma biblioteca JavaScript, e gosta de trabalhar com dados como arrays. Então, vamos converter o hash do Ruby em uma matriz JSON para uso do JavaScript no navegador.\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\nEstamos simplesmente iterando em cada par chave-valor no objeto e colocando-o em uma nova matriz. A razão pela qual não fizemos isso anteriormente foi porque não queríamos iterar no objeto `language_obj` durante a criação dele.\n\nAgora, *lang\\_freq.erb* vai precisar de um pouco de JavaScript para ajudar na renderização de um gráfico de barras.\nPor enquanto, basta usar o código fornecido aqui e ver os recursos vinculados acima se você deseja saber mais sobre como o D3 funciona:\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\nUfa! Novamente, não se preocupe com o que a maior parte deste código está fazendo. A parte relevante aqui é uma linha na parte superior, `var data = <%= languages %>;`, que indica que estamos transmitindo nossa matriz `languages` já criada para o ERB para manipulação.\n\nComo o guia \"D3 para mortais\" sugere, essa não é necessariamente a melhor forma de usar o D3. No entanto, serve para ilustrar como você pode usar a biblioteca, com o Octokit, para fazer coisas realmente incríveis.\n\n## Combinar diferentes chamadas de API\n\nAgora é hora de fazer uma confissão: o atributo `language` nos repositórios identifica apenas a linguagem \"primária\" definida. Isso significa que se você tiver um repositório que combina várias linguagens, aquela que tiver mais bytes de código será considerada a linguagem primária.\n\nVamos combinar algumas chamadas à API para obter uma *verdadeira* representação de qual linguagem tem o maior número de bytes escritos em todo o código. Um [mapa de árvore](https://www.d3-graph-gallery.com/treemap.html) deve ser uma ótima forma de visualizar os tamanhos das linguagens de codificação usadas, em vez de apenas a contagem. Precisaremos construir uma matriz de objetos parecida com esta:\n\n```json\n[ { \"name\": \"language1\", \"size\": 100},\n  { \"name\": \"language2\", \"size\": 23}\n  ...\n]\n```\n\nJá que já temos uma lista de repositórios acima, vamos inspecionar cada um e chamar o endpoint [GET /repos/{owner}/{repo}/languages](/pt/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\nA partir daí, adicionaremos cumulativamente cada idioma encontrado a uma lista de idiomas.\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\nEm seguida vamos formatar o conteúdo em uma estrutura que o D3 entende:\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(Para obter mais informações sobre a mágica do mapa de árvore D3, confira [este tutorial simples](/pt/rest/repos/repos#list-repository-languages).)\n\nPara concluir, passamos esta informação JSON para o mesmo modelo de ERB:\n\n```ruby\nerb :lang_freq, :locals => { :languages => languages.to_json, :language_byte_count => language_bytes.to_json}\n```\n\nComo já fizemos, aqui está um monte de JavaScript que você pode soltar diretamente no modelo:\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 voila! São lindos retângulos que contém suas linguagens de repositório, com proporções referentes de que são fáceis de serem vistos rapidamente. Talvez você precise ajustar a altura e a largura do mapa de árvore, transmitido como os dois primeiros argumentos para `drawTreemap` acima, a fim de mostrar todas as informações corretamente."}