Connectez et visualisez toutes vos données dans Looker Studio

1. Introduction

Looker Studio vous permet de créer sans frais des tableaux de bord interactifs et dynamiques avec de magnifiques visualisations de données. Récupérez vos données à partir de différentes sources et créez des rapports illimités dans Looker Studio, avec des fonctionnalités complètes de modification et de partage. La capture d'écran suivante montre un exemple de tableau de bord Looker Studio :

2f296fddf6af7393.png

( Cliquez ici pour afficher cet exemple de rapport dans Looker Studio)

Les connecteurs de communauté sont une fonctionnalité de Looker Studio qui vous permet d'utiliser Apps Script pour créer des connecteurs vers n'importe quelle source de données accessible sur Internet. Les connecteurs de communauté sont créés par la communauté Looker Studio. Cela signifie que tout le monde peut créer des connecteurs de communauté. Vous pouvez également partager des connecteurs de communauté avec d'autres personnes afin qu'elles puissent accéder à leurs propres données depuis Looker Studio.

Vous pouvez utiliser les connecteurs de communauté dans différents cas d'utilisation :

  • Vous visualisez des données provenant d'une plate-forme commerciale (réseaux sociaux, marketing, analytics, etc.).
  • Vous visualisez des données d'entreprise sur site (par exemple, des données de vente provenant d'une base de données MySQL sur site).
  • Vous permettez à vos clients de visualiser les données de votre service.
  • Vous créez une plate-forme de création de rapports à l'aide d'un bouton-poussoir.
  • Vous visualisez vos propres données à partir d'une source Web (par exemple, vous créez votre tableau de bord Google Fit).

Points abordés

  • Fonctionnement d'un connecteur de communauté Looker Studio
  • Utiliser Google Apps Script pour créer un connecteur de communauté
  • Utiliser les connecteurs de communauté dans Looker Studio

Prérequis

  • Accès à Internet et à un navigateur Web
  • Un compte Google
  • Connaissances de base en JavaScript et en API Web

2. Enquête rapide

Pourquoi avez-vous choisi cet atelier de programmation ?

Je m'intéresse à la visualisation des données en général. Je souhaite en savoir plus sur Looker Studio. Je souhaite créer mon propre connecteur de communauté. J'essaie d'intégrer Looker Studio à une autre plate-forme. Je suis intéressé par les solutions Google Cloud.

Comment prévoyez-vous d'utiliser cet atelier de programmation/tutoriel ?

Je vais le lire en diagonale uniquement Je vais le lire et effectuer les exercices

Comment évalueriez-vous votre niveau de connaissance de Looker Studio ?

Je n'en ai jamais entendu parler. Je sais ce que c'est, mais je ne l'utilise pas. Je l'utilise régulièrement. Je suis un utilisateur expérimenté.

Quelle est votre formation ?

Développeur Analyste commercial / financier / de données Data Scientist / Data Engineer Expert en marketing / réseaux sociaux / analyse numérique Designer Autre

Vous pouvez passer à la page suivante pour envoyer les informations de l'enquête.

3. Présentation des connecteurs de communauté

Les connecteurs de communauté Looker Studio permettent d'établir des connexions directes entre Looker Studio et n'importe quelle source de données accessible sur Internet. Vous pouvez vous connecter à des plates-formes commerciales, à des ensembles de données publics ou à vos propres données privées sur site. Les connecteurs de communauté peuvent extraire des données via des API Web, des API JDBC, des fichiers plats (CSV, JSON, XML) et des services Apps Script.

b25b8d6bea6da54b.png

Imaginons que vous ayez publié un package sur npm et que vous souhaitiez suivre le nombre de téléchargements du package au fil du temps, jour après jour. Dans cet atelier de programmation, vous allez créer un connecteur de communauté qui récupère des données à l'aide de l'API de décompte des téléchargements de packages npm. Le connecteur de communauté peut ensuite être utilisé dans Looker Studio pour créer un tableau de bord permettant de visualiser le nombre de téléchargements.

4. Workflow des connecteurs de communauté

Dans un connecteur de communauté de base, vous définissez quatre fonctions :

  • getAuthType()
  • getConfig()
  • getSchema()
  • getData()

En fonction de l'étape actuelle du workflow, Looker Studio exécute ces fonctions de connecteur et utilise la réponse dans les étapes suivantes. La vidéo ci-dessous présente les éléments suivants :

  • Fonctionnement d'un connecteur de communauté
  • Différentes étapes du workflow
  • Lorsque différentes fonctions sont appelées
  • Quand Looker Studio affiche différentes interfaces utilisateur
  • Actions utilisateur attendues à différentes étapes

Vous pourrez reprendre l'atelier de programmation après avoir regardé la vidéo.

Vous n'avez pas besoin de mémoriser ce workflow. Il vous suffit de le consulter pour comprendre ce qui se passe dans un connecteur. Vous pourrez toujours revenir à ce diagramme.

cc6688bf38749e5.png

À l'étape suivante, vous allez commencer à créer votre connecteur dans Google Apps Script. Vous devrez basculer entre l'UI Apps Script et cet atelier de programmation.

5. Configurer votre projet Apps Script

Étape 1 : Accédez à Google Apps Script.

Étape 2 : Créez un projet Apps Script en cliquant sur + Nouveau projet en haut à gauche.

fb12d7318d0946cf.png

Vous verrez un projet shell avec une fonction myFunction vide dans le fichier Code.gs.

b245ce5eb3dd2ee2.png

Étape 3 : Supprimez la fonction myFunction.

Étape 4 : Donnez un nom au projet :

  1. Cliquez sur Untitled project en haut à gauche de la page.
  2. Saisissez un titre pour le projet.

7172aebc181c91d4.png

Commencez à écrire le code de votre connecteur dans le fichier Code.gs.

6. Définir getAuthType()

Looker Studio appellera la fonction getAuthType() lorsqu'il aura besoin de connaître la méthode d'authentification utilisée par le connecteur. Cette fonction doit renvoyer la méthode d'authentification requise par le connecteur pour autoriser le service tiers.

Pour le connecteur de téléchargement npm que vous créez, vous n'avez pas besoin de vous authentifier auprès d'un service tiers, car l'API que vous utilisez ne nécessite aucune authentification. Copiez le code suivant et ajoutez-le à votre fichier Code.gs :

Code.gs

var cc = DataStudioApp.createCommunityConnector();

function getAuthType() {
  var AuthTypes = cc.AuthType;
  return cc
    .newAuthTypeResponse()
    .setAuthType(AuthTypes.NONE)
    .build();
}

Ici, vous indiquez que votre connecteur ne nécessite aucune authentification tierce (AuthTypes.NONE). Pour afficher toutes les méthodes d'authentification compatibles, consultez la documentation de référence sur AuthType().

7. Définir getConfig()

Les utilisateurs de votre connecteur devront le configurer avant de pouvoir l'utiliser. La réponse de la fonction getConfig() définit les options de configuration que les utilisateurs verront. Looker Studio appelle la fonction getConfig() pour obtenir les détails de configuration du connecteur. En fonction de la réponse fournie par getConfig(), Looker Studio affichera l'écran de configuration du connecteur et modifiera certains comportements du connecteur.

Sur l'écran de configuration, vous pouvez fournir des informations ou obtenir des saisies utilisateur à l'aide des éléments de formulaire suivants :

TEXTINPUT

Élément d'entrée

Zone de texte sur une seule ligne.

TEXTAREA

Élément d'entrée

Zone de texte multiligne.

SELECT_SINGLE

Élément d'entrée

Menu déroulant pour les options à sélection unique.

SELECT_MULTIPLE

Élément d'entrée

Menu déroulant pour les options de sélection multiple.

CHECKBOX

Élément d'entrée

Case à cocher unique permettant de capturer des valeurs booléennes.

INFO

Élément display

Il s'agit d'une zone de texte brut statique qui peut être utilisée pour fournir des instructions ou des informations à l'utilisateur.

Utilisez l'élément INFO pour fournir des instructions à l'utilisateur et un élément TEXTINPUT pour obtenir le nom du package d'entrée de l'utilisateur. Dans la réponse getConfig(), vous allez regrouper ces éléments de formulaire sous la clé configParams.

Étant donné que l'API à laquelle vous vous connectez nécessite une date comme paramètre, définissez dateRangeRequired sur true dans la réponse getConfig(). Cela indique à Looker Studio de fournir des plages de dates avec toutes les demandes de données. Si votre source de données n'a pas besoin de la date comme paramètre, vous pouvez l'omettre.

Ajoutez le code getConfig() suivant à votre fichier Code.gs, sous le code existant pour getAuthType() :

Code.gs

function getConfig(request) {
  var config = cc.getConfig();
  
  config.newInfo()
    .setId('instructions')
    .setText('Enter npm package names to fetch their download count.');
  
  config.newTextInput()
    .setId('package')
    .setName('Enter a single package name')
    .setHelpText('e.g. googleapis or lighthouse')
    .setPlaceholder('googleapis');
  
  config.setDateRangeRequired(true);
  
  return config.build();
}

En fonction de ces configParams, lorsque vous utilisez le connecteur dans Looker Studio, vous devriez voir un écran de configuration semblable à celui ci-dessous. Mais nous y reviendrons plus tard.

7de872f17e59e92.png

Passons à la fonction suivante : getSchema().

8. Définir getSchema()

Looker Studio appelle la fonction getSchema() pour obtenir le schéma associé à la configuration sélectionnée par l'utilisateur pour le connecteur. En fonction de la réponse fournie par getSchema(), Looker Studio affichera l'écran des champs à l'utilisateur, listant tous les champs du connecteur.

Pour toute configuration spécifique de votre connecteur, le schéma est une liste de tous les champs pour lesquels le connecteur peut fournir des données. Un connecteur peut renvoyer un schéma différent avec des champs différents en fonction de diverses configurations. Le schéma peut contenir des champs que vous récupérez à partir de votre source d'API, des champs que vous calculez dans Apps Script et des champs calculés dans Looker Studio à l'aide d'une formule de champ calculé. Votre connecteur fournit les métadonnées de chaque champ du schéma, y compris :

  • Nom du champ
  • Type de données du champ
  • Informations sémantiques

Pour en savoir plus, consultez la documentation de référence sur getSchema() et Field.

Selon la façon dont votre connecteur récupère les données, le schéma peut être fixe ou calculé de manière dynamique lorsque getSchema() est appelé. Les paramètres de configuration de getConfig() définis par l'utilisateur seront fournis dans l'argument request pour la fonction getSchema().

Pour cet atelier de programmation, vous n'avez pas besoin d'accéder à l'argument request. Vous en apprendrez davantage sur l'argument request lorsque vous écrirez du code pour la fonction getData() dans le prochain segment.

Pour votre connecteur, le schéma est fixe et contient les trois champs suivants :

packageName

Nom du package npm fourni par l'utilisateur

downloads

Nombre de téléchargements du package npm

day

Date du nombre de téléchargements

Vous trouverez ci-dessous le code getSchema() de votre connecteur. La fonction d'assistance getFields() abstrait la création des champs, car cette fonctionnalité est nécessaire à la fois pour getSchema() et getData(). Ajoutez le code suivant à votre fichier Code.gs :

Code.gs

function getFields(request) {
  var cc = DataStudioApp.createCommunityConnector();
  var fields = cc.getFields();
  var types = cc.FieldType;
  var aggregations = cc.AggregationType;
  
  fields.newDimension()
    .setId('packageName')
    .setType(types.TEXT);
  
  fields.newMetric()
    .setId('downloads')
    .setType(types.NUMBER)
    .setAggregation(aggregations.SUM);
  
  fields.newDimension()
    .setId('day')
    .setType(types.YEAR_MONTH_DAY);
  
  return fields;
}

function getSchema(request) {
  var fields = getFields(request).build();
  return { schema: fields };
}

En fonction de ce schéma, vous devriez voir les champs suivants sur l'écran des champs Looker Studio lorsque vous utilisez le connecteur dans Looker Studio. Nous y reviendrons plus tard lorsque vous testerez votre connecteur.

c7cd7057b202be59.png

Passons à notre dernière fonction, getData().

9. Définir getData() : partie 1

Looker Studio appelle la fonction getData() chaque fois qu'il doit récupérer des données. En fonction de la réponse fournie par getData(), Looker Studio affichera et mettra à jour les graphiques du tableau de bord. getData() peut être appelé lors des événements suivants :

  • L'utilisateur ajoute un graphique au tableau de bord.
  • L'utilisateur modifie un graphique
  • L'utilisateur consulte le tableau de bord
  • L'utilisateur modifie un filtre ou un contrôle des données associé.
  • Looker Studio a besoin d'un échantillon de données

Vous n'avez pas besoin de copier de code sur cette page, car vous copierez le code complet.

getData()

code ultérieurement.

Comprendre l'objet request

Looker Studio transmet l'objet request à chaque appel getData(). Examinez la structure de l'objet request ci-dessous. Cela vous aidera à écrire le code de votre fonction getData().

Structure de l'objet request

{
  configParams: object,
  scriptParams: object,
  dateRange: {
    startDate: string,
    endDate: string
  },
  fields: [
    {
      name: Field.name
    }
  ]
}
  • L'objet configParams contient les valeurs de configuration des paramètres définis dans getConfig() et configurés par l'utilisateur.
  • L'objet scriptParams contient des informations pertinentes pour l'exécution du connecteur. Vous n'en aurez pas besoin pour cet atelier de programmation.
  • dateRange contient la plage de dates demandée si elle est demandée dans la réponse getConfig().
  • fields contient la liste des noms des champs pour lesquels des données sont demandées.

Pour votre connecteur, un exemple de request de la fonction getData() peut se présenter comme suit :

{
  configParams: {
    package: 'jquery'
  },
  dateRange: {
    startDate: '2017-07-16',
    endDate: '2017-07-18'
  },
  fields: [
    {
      name: 'day',
    },
    {
      name: 'downloads',
    }
  ]
}

Pour l'appel getData() dans le request ci-dessus, seuls deux champs sont demandés, même si le schéma du connecteur comporte des champs supplémentaires. La page suivante contient l'exemple de réponse pour cet appel getData() et la structure générale de la réponse getData().

10. Définir getData() : partie 2

Dans la réponse getData(), vous devrez fournir à la fois le schéma et les données pour les champs demandés. Vous allez diviser le code en trois segments :

  • Créez un schéma pour les champs demandés.
  • Récupérez et analysez les données de l'API.
  • Transformer les données analysées et filtrer les champs demandés.

Vous n'avez pas besoin de copier de code sur cette page, car vous copierez le code complet.

getData()

code sur la page suivante.

Voici la structure de getData() pour votre connecteur.

function getData(request) {

  // TODO: Create schema for requested fields.
  
  // TODO: Fetch and parse data from API.
  
  // TODO: Transform parsed data and filter for requested fields.

  return {
    schema: <filtered schema>,
    rows: <transformed and filtered data>
  };
}

Créer un schéma pour les champs demandés

// Create schema for requested fields
  var requestedFieldIds = request.fields.map(function(field) {
    return field.name;
  });
  var requestedFields = getFields().forIds(requestedFieldIds);

Récupérer et analyser les données d'une API

L'URL de l'API npm se présente comme suit :

https://api.npmjs.org/downloads/point/{start_date}:{end_date}/{package}

Créez l'URL de l'API à l'aide des request.dateRange.startDate, request.dateRange.endDate et request.configParams.package fournis par Looker Studio. Récupérez ensuite les données de l'API à l'aide de UrlFetchApp(classe Apps Script : référence). Analysez ensuite la réponse récupérée.

  // Fetch and parse data from API
  var url = [
    'https://api.npmjs.org/downloads/range/',
    request.dateRange.startDate,
    ':',
    request.dateRange.endDate,
    '/',
    request.configParams.package
  ];
  var response = UrlFetchApp.fetch(url.join(''));
  var parsedResponse = JSON.parse(response).downloads;

Transformer les données analysées et filtrer les champs demandés

La réponse de l'API npm sera au format suivant :

{
  downloads: [
    {
    day: '2014-02-27',
    downloads: 1904088
    },
    ..
    {
    day: '2014-03-04',
    downloads: 7904294
    }
  ],
  start: '2014-02-25',
  end: '2014-03-04',
  package: 'somepackage'
}

Transforme la réponse de l'API npm et fournit la réponse getData() au format suivant. Si ce format n'est pas clair, consultez l'exemple de réponse dans le paragraphe suivant.

{
  schema: [
    {
      object(Field)
    }
  ],
  rows: [
    {
      values: [string]
    }
  ]
}

Dans la réponse, renvoyez le schéma uniquement pour les champs demandés à l'aide de la propriété schema. Vous renverrez les données à l'aide de la propriété rows sous forme de liste de lignes. Pour chaque ligne, la séquence de champs dans values doit correspondre à celle de schema. En nous basant sur l'exemple précédent de request, voici à quoi ressemblera la réponse pour getData() :

{
  schema: requestedFields.build(),
  rows: [
    {
      values: [ 38949, '20170716']
    },
    {
      values: [ 165314, '20170717']
    },
    {
      values: [ 180124, '20170718']
    },
  ]
}

Vous avez déjà créé le sous-ensemble du schéma. Utilisez la fonction suivante pour transformer les données analysées et les filtrer pour les champs demandés.

function responseToRows(requestedFields, response, packageName) {
  // Transform parsed data and filter for requested fields
  return response.map(function(dailyDownload) {
    var row = [];
    requestedFields.asArray().forEach(function (field) {
      switch (field.getId()) {
        case 'day':
          return row.push(dailyDownload.day.replace(/-/g, ''));
        case 'downloads':
          return row.push(dailyDownload.downloads);
        case 'packageName':
          return row.push(packageName);
        default:
          return row.push('');
      }
    });
    return { values: row };
  });
}

11. Définir getData() : partie 3

Le code getData() combiné ressemblera à celui ci-dessous. Ajoutez le code suivant à votre fichier Code.gs :

Code.gs

function responseToRows(requestedFields, response, packageName) {
  // Transform parsed data and filter for requested fields
  return response.map(function(dailyDownload) {
    var row = [];
    requestedFields.asArray().forEach(function (field) {
      switch (field.getId()) {
        case 'day':
          return row.push(dailyDownload.day.replace(/-/g, ''));
        case 'downloads':
          return row.push(dailyDownload.downloads);
        case 'packageName':
          return row.push(packageName);
        default:
          return row.push('');
      }
    });
    return { values: row };
  });
}

function getData(request) {
  var requestedFieldIds = request.fields.map(function(field) {
    return field.name;
  });
  var requestedFields = getFields().forIds(requestedFieldIds);

  // Fetch and parse data from API
  var url = [
    'https://api.npmjs.org/downloads/range/',
    request.dateRange.startDate,
    ':',
    request.dateRange.endDate,
    '/',
    request.configParams.package
  ];
  var response = UrlFetchApp.fetch(url.join(''));
  var parsedResponse = JSON.parse(response).downloads;
  var rows = responseToRows(requestedFields, parsedResponse, request.configParams.package);

  return {
    schema: requestedFields.build(),
    rows: rows
  };
}

Vous avez terminé avec le fichier Code.gs ! Mettez ensuite à jour le fichier manifeste.

12. Mettre à jour le fichier manifeste

Dans l'éditeur Apps Script, sélectionnez Paramètres du projet > Afficher le fichier manifeste "appsscript.json" dans l'éditeur.

90a68a58bbbb63c4.png

Un fichier manifeste appsscript.json est alors créé.

1081c738d5d577a6.png

Remplacez votre fichier appscript.json par le code suivant :

appsscript.json

{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",

  "dataStudio": {
    "name": "npm Downloads - From Codelab",
    "logoUrl": "https://raw.githubusercontent.com/npm/logos/master/npm%20logo/npm-logo-red.png",
    "company": "Codelab user",
    "companyUrl": "https://developers.google.com/looker-studio/",
    "addonUrl": "https://github.com/googledatastudio/example-connectors/tree/master/npm-downloads",
    "supportUrl": "https://github.com/googledatastudio/community-connectors/issues",
    "description": "Get npm package download counts.",
    "sources": ["npm"]
  }
}

Enregistrez le projet Apps Script.

5701ece1c89415c.png

Félicitations ! Vous avez créé votre premier connecteur de communauté et il est prêt à être testé.

13. Tester votre connecteur dans Looker Studio

Utiliser le déploiement

Étape 1 : Dans l'environnement de développement Apps Script, cliquez sur Déployer > Tester les déploiements pour ouvrir la boîte de dialogue "Tester les déploiements".

3f57ea0feceb2596.png

Le déploiement par défaut, Head Deployment, s'affiche.

Étape 2 : Cliquez sur Copier pour copier l'ID de déploiement "Head".

Étape 3 : Pour charger votre connecteur dans Looker Studio, remplacez l'espace réservé <HEAD_DEPLOYMENT_ID> dans le lien suivant par l'ID de déploiement de tête de votre connecteur, puis suivez le lien dans votre navigateur :

https://lookerstudio.google.com/datasources/create?connectorId=<HEAD_DEPLOYMENT_ID>

Autoriser le connecteur

Première utilisation de Looker Studio : si vous n'avez jamais utilisé Looker Studio, vous serez invité à autoriser Looker Studio et à accepter les conditions d'utilisation. Terminez la procédure d'autorisation. Lorsque vous utilisez Looker Studio pour la première fois, une boîte de dialogue peut également s'afficher pour vous inviter à modifier vos préférences marketing. Abonnez-vous aux annonces produit si vous souhaitez recevoir par e-mail des informations sur les dernières fonctionnalités et mises à jour disponibles, ainsi que des annonces concernant le produit.

Une fois le connecteur chargé, vous serez invité à l'autoriser.

d7e66726a1e64c05.png

Cliquez sur "Autoriser" et accordez l'autorisation requise au connecteur.

Configurer le connecteur

Une fois l'autorisation accordée, l'écran de configuration s'affiche. Saisissez lighthouse dans la zone de saisie de texte, puis cliquez sur "Connect" (Connecter) en haut à droite.

ec7416d6dbeabc8f.png

Confirmer le schéma

L'écran des champs s'affiche. Cliquez sur Créer un rapport en haut à droite.

4a9084bd51d2fbb8.png

Créer votre tableau de bord

Vous serez alors dans l'environnement du tableau de bord Looker Studio. Cliquez sur Ajouter au rapport.

1ca21e327308237c.png

Dans Looker Studio, chaque fois qu'un utilisateur accède à un connecteur et ajoute une configuration, une source de données est créée dans son compte Looker Studio. Vous pouvez considérer une source de données comme une instanciation du connecteur basée sur une configuration spécifique. En fonction du connecteur et de la configuration sélectionnés par l'utilisateur, une source de données renvoie un tableau de données avec un ensemble spécifique de champs. Les utilisateurs peuvent créer plusieurs sources de données à partir du même connecteur. Une source de données peut être utilisée dans plusieurs rapports, et un même rapport peut utiliser plusieurs sources de données.

Ajoutez maintenant un graphique de série temporelle. Dans le menu, cliquez sur Insérer > Série temporelle. Placez ensuite la série temporelle dans le canevas. Vous devriez voir un graphique de série temporelle du nombre de téléchargements npm pour le package sélectionné. Ajoutez un sélecteur de filtre de date et affichez le tableau de bord comme indiqué ci-dessous.

4c076e07665f57aa.gif

Et voilà ! Vous venez de créer votre premier connecteur de communauté. Nous arrivons à la fin de cet atelier de programmation. Découvrons maintenant les prochaines étapes que vous pouvez suivre.

14. Étapes suivantes

Améliorer le connecteur que vous avez créé

Améliorez le connecteur que vous venez de créer :

  • Dans Looker Studio, si vous ne fournissez pas de nom de package dans l'écran de configuration de votre connecteur, un message d'erreur s'affiche lorsque vous dessinez le graphique de série temporelle. Essayez d'ajouter une validation des entrées ou une option par défaut à la configuration de votre connecteur.
  • Essayez d'ajouter la prise en charge de l'interrogation de plusieurs noms de packages en même temps dans la configuration de votre connecteur. Remarque : L'API de décompte des téléchargements de packages npm accepte l'entrée de plusieurs noms de packages séparés par une virgule.
  • Vous trouverez des solutions à ces deux problèmes dans le code de notre connecteur npm.

Exploiter tout le potentiel des connecteurs de communauté

Ressources supplémentaires

Vous trouverez ci-dessous diverses ressources pouvant vous aider à explorer plus en détail les aspects abordés dans cet atelier de programmation.

Type de ressource

Fonctionnalités utilisateur

Fonctionnalités pour les développeurs

Documentation

Centre d'aide

Documentation pour les développeurs

Actualités et nouveautés

Inscrivez-vous dans Looker Studio > Paramètres utilisateur.

Liste de diffusion pour les développeurs

Poser des questions

Forum des utilisateurs

Stack Overflow [looker-studio]

Vidéos

DataVis DevTalk

Exemples

Dépôt Open Source