Tableau Hyper API - l'équipe BI vous remerciera

Nous voulons vous expliquer comment nous avons aidé notre équipe BI à organiser un processus automatique de livraison de données au serveur Tableau à partir de MongoDB en utilisant le format de stockage de données tabloïd «hyper», et le processus de configuration de la génération de données est effectué via une interface Web simple.



Au début, nous décrirons brièvement à quoi ressemblait le processus avant et après avoir appris à notre produit interne A1 à collecter par programmation des sources de données et à les publier sur Tableau Server. Ensuite, nous allons regarder de plus près le problème de la commande BI et la solution trouvée, et aussi jeter un œil sous le capot (ici sur la création d'un fichier .hyper, la publication d'un fichier sur un serveur de tableau et la mise à jour d'un hyper). Bienvenue au chat!



Tableau Hyper API - l'équipe BI vous remerciera




Au sein du groupe publicitaire DAN, nous travaillons beaucoup avec les données de suivi publicitaire de Mediascope , un compteur industriel sur le marché des médias. Il existe différents scénarios: certains employés téléchargent des données brutes, d'autres utilisent des bases de données pré-traitées prêtes à l'emploi, et quelqu'un commande le développement de tableaux de bord automatisés basés sur ces données. Parlons plus en détail du dernier scénario: nos développeurs BI collectent des tableaux de bord dans Tableau, mais avant de commencer à «dessiner», ils doivent également apporter les données au format souhaité pour le développement.



Le chemin de vie des données, des matières premières aux magnifiques graphiques automatisés, peut être divisé en 4 étapes:



  1. Obtenir des données brutes
  2. Nettoyage et révision des données
  3. Création de sources de données pour Tableau
  4. Développement de visualisations


C'était



Avant d'apprendre à générer par programme des sources de données pour Tableau, le processus ressemblait à ceci:



Ancien processus de vie


1. Obtention de données brutes



Les utilisateurs génèrent des rapports tabulaires via l'outil interne A1. Nous en parlerons plus en détail ci-dessous.



2. Nettoyage et modification des données La



capacité de transformation des données est également incluse dans l'outil A1, après quoi les données nettoyées peuvent être téléchargées vers xslx / csv et continuer à travailler avec elles en dehors de l'outil. Il convient de noter ici que certains utilisateurs se limitent au 1er point et, après avoir téléchargé les rapports, modifient eux-mêmes les données.



3. Création de sources de données pour Tableau



Auparavant, les clients du tableau de bord étaient livrés avec un ensemble d'Excel, qu'ils généraient dans les paragraphes précédents. Et les développeurs BI ont intégré ces ex-works dans une seule source de données (argot tabloïd). Il n'était pas toujours possible de se limiter aux seuls outils Tableau; ils écrivaient souvent des scripts en Python.



4. Développement de visualisations



Enfin, la pointe de l'iceberg crée un tableau de bord et le publie sur Tableau Server, là où le client le verra. En pratique, le rendu prend souvent moins de temps que la collecte de données et la configuration des mises à jour.



La douleur s'est accumulée à la troisième étape, à mesure que le nombre de solutions personnalisées augmentait, dont la maintenance et la mise en œuvre étaient coûteuses. De plus, des erreurs dans les données de la deuxième étape fuyaient régulièrement - l'Excel intermédiaire entre les deux systèmes (A1 et Tableau) poussait l'utilisateur: «corrigeons quelque chose avec des stylos, personne ne le remarquera».



Est devenu



La tâche principale était d'éliminer les ex-fureurs entre les étapes 2 et 3. En conséquence, nous avons appris à A1 à collecter des sources de données et à les publier sur Tableau Server. Voici ce qui s'est passé:



Nouveau processus de vie


Désormais, les étapes 1 à 3 ont lieu dans A1, à la sortie, l'équipe de BI reçoit une source de données publiée sur Tableau Server pour développer des visualisations. L'Hyper API est devenu le lien de connexion, qui sera discuté plus loin.



résultats



Réduction du nombre de nœuds lorsque vous travaillez dans différents outils. Maintenant, il est plus difficile de faire une erreur quelque part dans le processus, et il est plus facile de repérer où l'erreur s'est produite, l'enquête sur les défaillances prend moins de temps. Le système avertit les utilisateurs des erreurs courantes.



Libérez le temps de l'équipe BI . Auparavant, il y avait peu de solutions de modèles et de nombreuses personnalisations. Le plus souvent, un traitement en Python a été ajouté pour chaque projet. Dans de rares cas, où le traitement n'était pas nécessaire, nous avons travaillé directement dans Tableau Desktop (le principal outil de développement).



Maintenant, la préparation de la source de données est: cliquez sur les champs requis dans l'interface A1, marquez lesquels d'entre eux nous développons en lignes (si nécessaire) et définissez éventuellement le type de champs à l'avance.



Nous ne chargeons pas Tableau Servermise à jour de sources de données volumineuses - la mise à jour est effectuée par A1, et un hyper prêt à l'emploi est téléchargé sur le serveur.



* Bonus - nous encourageons les utilisateurs à travailler dans A1. Si auparavant certains utilisateurs, après avoir déchargé les rapports bruts, les ont modifiés manuellement en dehors de l'outil, maintenant, puisque tout le processus des étapes 1 à 3 se déroule en A1, il est plus facile pour les utilisateurs de configurer le processus de nettoyage là-bas.



Problème et solution



Un peu sur A1



Avant de commencer à parler de notre solution, nous devons parler de notre produit interne A1, auquel nous avons attaché la génération de sources de données.



A1 est un produit interne de l'entreprise, conçu pour simplifier le flux de travail des employés dont la fonction principale est la suivante:



  • Récupérer les données des produits logiciels MediaScope
  • Apportez (nettoyez) ces données sous une forme pratique pour les analystes sujets
  • Si nécessaire, préparez les données pour créer des tableaux de bord (nous en reparlerons aujourd'hui)


Une fois que l'utilisateur a terminé le nettoyage des données, elles sont stockées dans le système A1. Dans notre terminologie, cela s'appelle "Conteneur". Un conteneur est un document standard dans MongoDB, que nous devons transférer vers le serveur Tableau.



Problème d'équipe BI



Notre équipe de développement BI devait d'une manière ou d'une autre obtenir des données d'A1, qui étaient stockées dans MongoDB, et créer des tableaux de bord basés sur les données reçues. Tout d'abord, nous avons essayé de récupérer les données de MongoDB en utilisant des outils de tableau de bord réguliers, mais cela n'a pas résolu le problème:



  • Puisque les données sont stockées dans MongoDB, des données avec une structure arbitraire sont reçues à l'entrée du tableau de bord, ce qui signifie que vous devrez constamment maintenir cette logique.
  • Pour agréger les données de MongoDB, il était nécessaire de faire glisser certains enregistrements de la collection, et non de la collection entière - le pilote Tableau ne sait pas comment faire cela.
  • Entre autres choses, il ne suffisait pas d'obtenir les données: il fallait parfois les «étendre» - pour «décomposer» certaines colonnes en lignes. Ce qui, non plus, n'était pas si facile à faire, à partir du mot.


Qu'avons-nous trouvé



Il a été décidé d'essayer de résoudre ce problème avec mon vélo, en utilisant la bibliothèque d' API Tableau Hyper . Cette bibliothèque vous permet de créer un fichier au format .hyper, dans lequel il est facile d'ajouter des données, puis de l'utiliser comme source de données pour créer un tableau de bord sur la carte serveur.



Comme les développeurs du tableau de bord décrivent eux-mêmes l'hyper:

Hyper est un moteur de données en mémoire hautes performances qui aide les clients à analyser rapidement des ensembles de données volumineux ou complexes en évaluant efficacement les requêtes de base de données. Basé sur la plate-forme Tableau, Hyper utilise la génération de code dynamique propriétaire et des technologies de concurrence avancées pour atteindre des performances élevées dans les extraits et les requêtes.
Le processus approximatif de travail dans notre programme est le suivant:



  • L'utilisateur sélectionne les conteneurs et les colonnes souhaitées
  • Le système extrait les données des conteneurs
  • Sur la base des données reçues, le système détermine les types de colonnes
  • La création d'un hyper et l'insertion de données dans celui-ci est initialisée
  • L'hyper est chargé sur le serveur du tableau de bord
  • Les développeurs BI voient l'hyper sur le serveur et créent un tableau de bord basé sur celui-ci


Lorsque de nouvelles données sont versées dans les conteneurs, le système recevra un signal indiquant que l'hyper doit être mis à jour:



  • Le système téléchargera l'hyper à partir du serveur de tableau de bord
  • Prendra de nouvelles données de MongoDB et mettra à jour l'hyper
  • Après cela, le système télécharge un nouvel hyper sur le serveur, écrasant l'existant
  • L'utilisateur a juste besoin de cliquer sur le bouton "Actualiser" pour afficher des informations à jour dans le tableau de bord


Ce que voit l'utilisateur



Comme indiqué précédemment, A1 est une application Web. Nous avons utilisé Vue.js et Vuetify pour créer un service d'hyper génération front-end.



L'interface de l'application est divisée en trois écrans.



Écran de sélection des conteneurs



Sur le premier écran, l'utilisateur sélectionne les conteneurs et colonnes souhaités.



Si l'option "Unpivot" est activée, deux colonnes supplémentaires seront créées dans l'hyper: variable - les noms des colonnes sélectionnées par la colonne Métriques et les valeurs - les valeurs de ces colonnes.



La colonne Dimension ajoute une colonne avec la colonne sélectionnée du même nom à l'hyper. Le nombre de colonnes sélectionnées Les dimensions et leurs noms doivent être les mêmes dans tous les conteneurs afin que l'intégrité de la table dans l'hyper ne soit pas violée, il existe donc une colonne "Hyper nom", qui vous permet de spécifier le nom de la colonne sélectionnée si elles sont nommées différemment dans les conteneurs.



Journaux de processus d'hyper création



Ceci conclut le processus de configuration de l'hyiper. L'utilisateur a juste besoin d'aller au deuxième écran, de cliquer sur "Créer hyper" et de suivre la progression des événements dans les journaux.



Paramètres additionnels



Le troisième écran contient des paramètres supplémentaires:



  • Vous pouvez activer l'ignorance des mises à jour si nous n'avons pas besoin du système pour mettre à jour automatiquement l'hyper
  • Vous pouvez spécifier un e-mail pour envoyer des rapports de mise à jour
  • Vous pouvez spécifier manuellement le type de données pour la colonne de valeurs (utilisé uniquement en mode non pivot): float, string, ou automatiquement déterminé par le système (nous parlerons des types plus loin)
  • Vous pouvez également spécifier des types de données pour les colonnes sélectionnées dans les conteneurs.


Qu'y a-t-il sous le capot



A1 est écrit en Python. Pour travailler avec des données, nous utilisons des pandas, et nous sérialisons les données des pandas pour les décaper et les stocker dans MongoDB GridFS.



Lorsqu'une commande de création d'un hyper est reçue, le système effectue les opérations suivantes:



  • Décharge tous les conteneurs nécessaires de MongoDB et désérialise les données en pandas datafremes
  • Prépare les données: ne laisse que les colonnes requises dans les dataframes, leur donne de nouveaux noms, développe les tableaux si nécessaire via pandas.melt
  • Si l'utilisateur a défini le type de données pour les colonnes, convertissez les données en float32 ou en chaîne
  • Après tout le travail préparatoire avec les données, le système crée un fichier via hyper api et envoie le fichier au serveur de tableau de bord via tabcmd.


Cela vaut la peine de parler un peu des types de données des colonnes. L'une des caractéristiques du stockage de données dans des conteneurs A1 est que les utilisateurs ne se soucient pas des types à attribuer aux colonnes, les pandas le font parfaitement pour eux: le système gère calmement les situations où des nombres et des valeurs de chaîne sont présents dans une colonne. Cependant, l'hyper n'aime pas ça: si vous lui dites que la colonne doit être de type int, le système jurera en essayant d'insérer autre chose qu'un entier. Par conséquent, il a été décidé de n'utiliser que deux types de données dans les hypers: string et float.



Donc, nous avons compris le principe général du travail, parlons de travailler avec l'hyper lui-même.



Créer un fichier .hyper



Pour travailler avec Hyper API, vous devez installer la bibliothèque, vous pouvez la télécharger depuis le site officiel ici . Il existe également de bons exemples de la manière de travailler avec cet outil. Nous en indiquerons brièvement les principaux points.



Le fichier hyiper lui-même est une sorte de base de données, qui rappelle un peu SQLite. Grâce à l'API, vous pouvez accéder aux données en utilisant la syntaxe SQL suivante:



f"SELECT {escape_name('Customer ID')} FROM {escape_name('Customer')}"


Puisque notre système est écrit en Python, nous utiliserons également la bibliothèque pour le langage correspondant. Lors de la création du fichier, nous devons spécifier le nom du schéma, le nom de la table et les colonnes avec les types. Le nom du schéma et de la table doit s'appeler «Extraire», car c'est dans ce schéma avec la table que Tableau Server grimpe pour extraire les données des livres.



with HyperProcess(Telemetry.SEND_USAGE_DATA_TO_TABLEAU) as hyper:
    with Connection(
        hyper.endpoint, self.fullpath_hyper, CreateMode.CREATE_AND_REPLACE
    ) as connection:
        connection.catalog.create_schema("Extract")
        main_table = TableName("Extract", "Extract")
        example_table = TableDefinition(main_table)


Après avoir créé la table, nous devons créer des colonnes et définir des types. Comme nous l'avons dit précédemment, nos données n'ont que deux types (flottant ou chaîne), donc en fonction du type de colonnes dans le dataframe, nous définissons ceci pour les colonnes:



for column in dataframe.columns:
    if dataframe[column].dtype.name in ("category", "object"):
        example_table.add_column(TableDefinition.Column(column, SqlType.text()))

    elif dataframe[column].dtype.name in ("float32"):
        example_table.add_column(
            TableDefinition.Column(column, SqlType.double())
        )

    connection.catalog.create_table(example_table)


Après avoir créé la table, vous pouvez insérer des données:



with Inserter(connection, example_table) as inserter:
    for val in dataframe.values:
        inserter.add_row(val.tolist())
    inserter.execute()


Ici, nous parcourons le dataframe ligne par ligne et accumulons la liste avec des valeurs via inserter.add_row () . En fait, il existe une fonction add_rows () dans l'api de l'hyper , qui prend une liste de listes et insère déjà les valeurs. Pourquoi cela n'a-t-il pas été fait? Pour économiser de la RAM: afin de fournir une liste de listes de valeurs à partir du dataframe, vous devez demander aux pandas de faire values.tolist () . Et lorsque vous avez 150 millions de lignes de données, cela s'avère être une opération très coûteuse pour la RAM, bien que cela n'affecte en rien les performances (en tout cas, il n'a pas été remarqué qu'en raison de l'itération itérative sur les lignes, la vitesse de création d'un hyper a coulé en quelque sorte). De plus, add_rows ()fonctionne comme le sucre syntaxique: il prend en fait une liste de listes et ajoute des données de manière itérative.



Ceci conclut la création de notre hyper. Ensuite, nous devons le publier sur le serveur.



Publication d'un fichier sur un serveur de tableau



Pour accéder au tableau-server, nous utiliserons l'utilitaire tabcmd - il s'agit d'un utilitaire de console qui vous permet de vous connecter au serveur et d'effectuer des fonctions administratives - créer des utilisateurs, des groupes, des livres, etc.



Nous exécuterons la commande tabcmd via le sous-processus Python.



popen = subprocess.Popen(
    f'/opt/tableau/tabcmd/bin/tabcmd publish "{fullpath_hyper}" -n "{filename}" -o -r "A1_test" '
    '-s http://tableau.domain.com -u "username" -p "password" --no-certcheck',
    shell=True,
    stderr=subprocess.PIPE,
    stdout=subprocess.PIPE,
)
return_code = popen.wait()
if return_code:
    error = str(popen.communicate()[1])
    return f"     . {error}"


Nous passons la commande et les touches suivantes à tabcmd:



  • publier : télécharger un fichier sur le serveur
  • -n (--name) : quel nom de fichier sera sur le serveur
  • -o (--overwrite) : s'il y a un fichier avec ce nom, alors écraser
  • -r “A1_test” (--project): ( )
  • -s (--server): tableau-
  • -u -p:
  • --no-certcheck: SSL-




Nous avons compris comment créer un nouvel hyper, mais que faire quand l'hyper se compose de dix conteneurs et que l'un d'eux a reçu de nouvelles données? Nous mettrons à jour l'hyper.



Lorsque de nouvelles données arrivent dans le conteneur, le système recherche s'il y a des hypers qui utilisent ce conteneur. Si tel est le cas, la tâche consiste à mettre à jour l'hyper.



Pour comprendre quelles données à partir de quel conteneur se trouvent dans l'hyper, le système crée également une colonne container_id supplémentaire lors de la création de l'hyper. Avec cette approche, la mise à jour devient très simple:



  • Nous prenons le fichier du serveur
  • Nous supprimons toutes les lignes de l'hyper, où container_id est égal au conteneur mis à jour
  • Insérer de nouvelles lignes
  • Téléchargez le fichier d'écrasement sur le serveur.


Le processus de récupération d'un fichier est légèrement différent du processus de téléchargement. Tout d'abord, nous ne prendrons pas le fichier .hyper du serveur, mais l'archive .tdsx, que nous décompresserons ensuite et ouvrirons le .hyper lui-même.



Afin de récupérer le fichier, nous utilisons tabcmd:



popen = subprocess.Popen(
    f'/opt/tableau/tabcmd/bin/tabcmd get "datasources/{filename_tdsx}" '
    f'-s http://tableau.domain.com -u "username" -p "password" '
    f'--no-certcheck -f "{fullpath_tdsx}"',
    shell=True,
    stderr=subprocess.PIPE,
    stdout=subprocess.PIPE,
)
return_code = popen.wait()
if return_code:
    error = str(popen.communicate()[1])
    return f". {error}"


Ici, nous utilisons la commande et les touches suivantes:



  • get : récupère un fichier du serveur. Si le fichier test.hyper est sur le serveur, vous devez vous référer au fichier test.tdsx, et ils sont tous dans le répertoire de la source de données (je ne pourrais pas google pourquoi une telle fonctionnalité de travail dans le tableau de bord, si vous savez, partagez dans les commentaires)
  • -f (--filename) : chemin complet, y compris le nom et l'extension du fichier, où enregistrer le fichier


Une fois le fichier téléchargé, il doit être décompressé via un fichier zip:



with zipfile.ZipFile(fullpath_tdsx, "r") as zip_ref:
    zip_ref.extractall(path)


Après la décompression, l'hyper sera dans le répertoire ./Data/Extracts .



Maintenant que nous avons la version actuelle du fichier, nous pouvons en supprimer les lignes inutiles:



table_name = TableName("Extract", "Extract")

with HyperProcess(Telemetry.SEND_USAGE_DATA_TO_TABLEAU) as hyper:
    with Connection(hyper.endpoint, self.fullpath_hyper) as connection:
        connection.execute_query(
            f"DELETE FROM {table_name} WHERE "
            f'{escape_name("container_id")}={container_id}'
        ).close()


Eh bien, l'insertion et la publication d'un fichier ont déjà été décrites ci-dessus.



Conclusion



Quelle est la ligne de fond? Après avoir effectué le travail sur l'implémentation de la génération d'hyper-fichiers et leur livraison automatique au tableau-serveur, nous avons considérablement réduit la charge de l'équipe BI, il est devenu plus facile de mettre à jour les données dans le tableau de bord et, surtout, plus rapide. La connaissance même de l'hyper api n'a pas été pénible, la documentation est bien écrite et l'intégration même de la technologie dans notre système a été facile.



Nous vous remercions de votre attention! Si vous avez des questions ou des commentaires, veuillez les laisser dans les commentaires.



L'article a été écrit conjointement avec Vasily Lavrov (VasilyFromOpenSpace) — -



All Articles