{"meta":{"title":"Rendern von Daten als Graphen","intro":"Hier erfährst du, wie du die Programmiersprachen aus deinem Repository mithilfe der D3.js-Bibliothek und Ruby Octokit visualisieren kannst.","product":"REST-API","breadcrumbs":[{"href":"/de/rest","title":"REST-API"},{"href":"/de/rest/guides","title":"Anleitungen"},{"href":"/de/rest/guides/rendering-data-as-graphs","title":"Rendern von Daten als Graphen"}],"documentType":"article"},"body":"# Rendern von Daten als Graphen\n\nHier erfährst du, wie du die Programmiersprachen aus deinem Repository mithilfe der D3.js-Bibliothek und Ruby Octokit visualisieren kannst.\n\nIn diesem Leitfaden verwenden wir die API, um Informationen zu Repositorys, die wir besitzen, und die Programmiersprachen, die sie bilden, zu fetchen. Anschließend werden wir diese Informationen auf verschiedene Weise mithilfe der [D3.js](https://d3js.org/)-Bibliothek visualisieren. Um mit der GitHub-API zu interagieren, verwenden wir die Ruby-Bibliothek [Octokit](https://github.com/octokit/octokit.rb).\n\nFalls du es noch nicht getan hast, solltest du den Leitfaden [Grundlagen der Authentifizierung](/de/apps/oauth-apps/building-oauth-apps/authenticating-to-the-rest-api-with-an-oauth-app) lesen, bevor du mit diesem Beispiele beginnst. Den vollständigen Quellcode für dieses Projekt findest du im Repository [platform-samples](https://github.com/github/platform-samples/tree/master/api/ruby/rendering-data-as-graphs).\n\nSpringen wir ins kalte Wasser!\n\n## Einrichten einer OAuth app\n\nZuerst [registrierst du eine neue Anwendung](https://github.com/settings/applications/new) für GitHub. Stelle die Haupt- und Rückruf-URLs auf `http://localhost:4567/` ein. Wie [zuvor](/de/apps/oauth-apps/building-oauth-apps/authenticating-to-the-rest-api-with-an-oauth-app) werden wir die Authentifizierung für die API behandeln, indem wir eine Rack-Middleware mithilfe von [sinatra-auth-github](https://rubygems.org/gems/sinatra_auth_github) implementieren:\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\nRichte eine ähnliche *config.ru*-Datei wie im vorherigen Beispiel ein:\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## Abrufen der Repository-Informationen\n\nDieses Mal verwenden wir die [Ruby-Bibliothek Octokit](https://github.com/octokit/octokit.rb), um mit der GitHub-API zu kommunizieren. Dies ist viel einfacher als eine Reihe von REST-Aufrufen. Darüber hinaus wurde Octokit von einem GitHubber entwickelt und wird aktiv gepflegt, damit du weißt, dass es funktioniert.\n\nDie Authentifizierung mit der API über Octokit ist einfach. Übergib einfach deine Anmeldedaten und dein Token an den `Octokit::Client`-Konstruktor:\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\nLass uns etwas Interessantes mit den Daten zu unseren Repositorys tun. Wir werden die verschiedenen Programmiersprachen sehen, die sie verwenden, und zählen, welche am häufigsten verwendet werden. Dazu benötigen wir zunächst eine Liste unserer Repositorys aus der API.\nMit Octokit sieht das wie folgt aus:\n\n```ruby\nrepos = client.repositories\n```\n\nAls Nächstes durchlaufen wir jedes Repository und zählen die Sprache, die GitHub damit verknüpft:\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\nWenn du den Server neu startest, sollte deine Webseite etwas anzeigen, das wie folgt aussieht:\n\n```ruby\n{\"JavaScript\"=>13, \"PHP\"=>1, \"Perl\"=>1, \"CoffeeScript\"=>2, \"Python\"=>1, \"Java\"=>3, \"Ruby\"=>3, \"Go\"=>1, \"C++\"=>1}\n```\n\nSo weit, so gut, aber nicht sehr menschenfreundlich. Eine Visualisierung wäre großartig, um uns zu helfen, zu verstehen, wie diese Sprachanzahl verteilt wird. Fügen wir unsere Anzahl in D3 ein, um ein übersichtliches Balkendiagramm zu erhalten, das die Beliebtheit der von uns verwendeten Sprachen darstellt.\n\n## Visualisierung der Anzahl von Sprachen\n\nD3.js oder kurz D3 ist eine umfassende Bibliothek zum Erstellen vieler Arten von Diagrammen, Graphen und interaktiven Visualisierungen.\nD3 im Detail zu benutzen, würde den Bereich dieser Anleitung sprengen, aber einen guten Einführungsartikel findest du in [D3 für Normalsterbliche](http://recursion.org/d3-for-mere-mortals/).\n\nD3 ist eine JavaScript-Bibliothek und arbeitet gerne mit Daten als Arrays. So konvertieren wir unseren Ruby-Hash für die Verwendung durch JavaScript im Browser in ein JSON-Array.\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\nWir durchlaufen einfach jedes Schlüsselwertpaar in unserem Objekt und pushen es in ein neues Array. Der Grund, aus dem wir dies zuvor nicht ausgeführt haben, besteht darin, dass wir unser `language_obj`-Objekt nicht durchlaufen wollten, während wir es erstellt haben.\n\nNun benötigt *lang\\_freq.erb* einige JavaScript-Dateien, um das Rendern eines Balkengraphen zu unterstützen.\nJetzt kannst du einfach den hier bereitgestellten Code verwenden und auf die oben verknüpften Ressourcen verweisen, wenn du mehr über die Funktionsweise von D3 erfahren möchtest:\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\nPuh! Noch einmal, mache dir sich keine Sorgen darüber, was der Großteil dieses Codes tut. Der relevante Teil hier ist eine Zeile am Anfang--`var data = <%= languages %>;`--die angibt, dass wir unser zuvor erstelltes `languages`-Array zur Manipulation an ERB übergeben.\n\nWie der Leitfaden „D3 für Sterbliche“ andeutet, ist dies nicht unbedingt die beste Anwendung von D3. Aber es dient dazu, zu veranschaulichen, wie du die Bibliothek zusammen mit Octokit verwenden kannst, um einige wirklich erstaunliche Dinge zu machen.\n\n## Kombinieren verschiedener API-Aufrufe\n\nJetzt ist es Zeit für ein Geständnis: Das `language`-Attribut innerhalb von Repositories identifiziert nur die als „primär“ definierte Sprache. Das bedeutet, wenn du über ein Repository verfügst, das mehrere Sprachen kombiniert, wird dies mit den meisten Bytes des Codes als primäre Sprache betrachtet.\n\nKombinieren wir einige API-Aufrufe, um eine *echte* Darstellung davon zu erhalten, in welcher Sprache die größte Anzahl von Bytes in unserem gesamten Code geschrieben wurde. Eine [Baumstruktur](https://www.d3-graph-gallery.com/treemap.html) sollte eine hervorragende Möglichkeit sein, die Größen unserer verwendeten Codierungssprachen statt nur die Anzahl zu visualisieren. Wir müssen ein Array von Objekten konstruieren, das wie in etwa so aussieht:\n\n```json\n[ { \"name\": \"language1\", \"size\": 100},\n  { \"name\": \"language2\", \"size\": 23}\n  ...\n]\n```\n\nDa wir bereits über eine Liste der Repositorys verfügen, sehen wir sie uns einmal genauer an und rufen den [Endpunkt „GET /repos/{owner}/{repo}/languages“](/de/rest/repos/repos#list-repository-languages) auf:\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\nVon dort aus fügen wir jede gefundene Sprache kumulativ einer Liste von Sprachen hinzu.\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\nDanach werden wir den Inhalt in eine Struktur formatieren, die D3 versteht:\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(Weitere Informationen zu D3 Tree Map Magic findest du in [diesem einfachen Lernprogramm](/de/rest/repos/repos#list-repository-languages).)\n\nZum Umschließen übergeben wir diese JSON-Informationen an dieselbe ERB-Vorlage:\n\n```ruby\nerb :lang_freq, :locals => { :languages => languages.to_json, :language_byte_count => language_bytes.to_json}\n```\n\nWie zuvor siehst du hier eine Reihe von JavaScripts, die du direkt in deine Vorlage ablegen kannst:\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! Schöne Rechtecke, die deine Repositorysprachen enthalten, mit relativen Proportionen, die auf einen Blick leicht zu erkennen sind. Möglicherweise musst du die Höhe und Breite deiner Treemap anpassen, die als die ersten beiden `drawTreemap`-Argumente oben übergeben wurden, damit alle Informationen korrekt angezeigt werden."}