Tableau Hyper API - a equipe de BI vai agradecer

Queremos contar a você como ajudamos nossa equipe de BI a organizar o processo automático de entrega de dados ao servidor Tableau do MongoDB usando o formato de armazenamento de dados tablóide "hyper", e o processo de configuração da geração de dados é realizado por meio de uma interface da web simples.



No início, descreveremos resumidamente como era o processo antes e depois de ensinarmos nosso produto interno A1 a coletar fontes de dados programaticamente e publicá-las no Tableau Server. Em seguida, examinaremos mais de perto o problema do comando BI e a solução encontrada, e também daremos uma olhada nos bastidores (aqui sobre como criar um arquivo .hyper, publicar um arquivo em um servidor tableau e atualizar um hyper). Bem-vindo ao gato!



Tableau Hyper API - a equipe de BI vai agradecer




Nós do grupo de publicidade DAN trabalhamos muito com dados de monitoramento de publicidade do Mediascope , um medidor industrial do mercado de mídia. Existem diferentes cenários: alguns funcionários carregam dados brutos, outros usam bancos de dados pré-processados ​​prontos e alguém ordena o desenvolvimento de painéis automatizados com base nesses dados. Vamos falar sobre o último cenário com mais detalhes - nossos desenvolvedores de BI coletam painéis no Tableau, mas antes de começar a "desenhar", eles também precisam trazer os dados para o formato desejado que seja conveniente para o desenvolvimento.



O percurso de vida dos dados, desde as matérias-primas até os belos gráficos automatizados, pode ser dividido em 4 etapas:



  1. Obtendo dados brutos
  2. Limpeza e revisão de dados
  3. Criação de fontes de dados para Tableau
  4. Desenvolvimento de visualizações


isso foi



Antes de aprendermos como gerar fontes de dados de maneira programática para o Tableau, o processo era assim:



Processo da velha vida


1. Obtendo dados brutos



Os usuários geram relatórios tabulares por meio da ferramenta interna A1. Falaremos sobre isso com mais detalhes a seguir.



2. Limpeza e modificação de dados A



capacidade de transformação de dados também está incluída na ferramenta A1, após a qual os dados limpos podem ser carregados para xslx / csv e continuar a trabalhar com eles fora da ferramenta. É importante destacar aqui que alguns usuários se limitam ao primeiro ponto e, após o upload dos relatórios, modificam os dados por conta própria.



3. Criação de fontes de dados para o Tableau



Anteriormente, os clientes do painel vinham com um conjunto de Excels, que eles geraram nos parágrafos anteriores. E os desenvolvedores de BI trouxeram esses ex-works em uma única fonte de dados (gíria de tablóide) por conta própria. Nem sempre foi possível nos limitarmos apenas às ferramentas do Tableau; elas geralmente escreviam scripts em Python.



4. Desenvolvimento de visualizações



Por fim, a ponta do iceberg é criar um painel e publicá-lo no Tableau Server, onde o cliente pode vê-lo. Na prática, a renderização geralmente leva menos tempo do que a coleta de dados e a configuração de atualizações.



A dor se acumulou na terceira etapa, conforme crescia o número de soluções personalizadas, cuja manutenção e implementação eram caras. Além disso, erros nos dados da segunda etapa vazavam regularmente - o Excel intermediário entre os dois sistemas (A1 e Tableau) pressionava o usuário: “vamos consertar algo com canetas, ninguém vai notar”.



Se tornou



A principal tarefa era eliminar ex-peles entre 2 e 3 etapas. Como resultado, ensinamos A1 a coletar fontes de dados e publicá-las no Tableau Server. Eis o que aconteceu:



Novo processo de vida


Agora, as etapas 1 a 3 ocorrem em A1, na saída a equipe de BI recebe uma fonte de dados publicada no Tableau Server para desenvolver visualizações. A Hyper API tornou-se o link de conexão, que será discutido mais adiante.



resultados



Reduziu o número de nós ao trabalhar em ferramentas diferentes. Agora é mais difícil cometer um erro em algum ponto do processo e é mais fácil detectar onde o erro ocorreu, a investigação de falhas leva menos tempo. O sistema avisa os usuários sobre erros comuns.



Libere tempo para a equipe de BI . Anteriormente, havia poucas soluções de modelo e muitas personalizações. Na maioria das vezes, o processamento em Python foi adicionado para cada projeto. Em casos raros, nos quais o processamento não era necessário, trabalhamos diretamente no Tableau Desktop (a principal ferramenta de desenvolvimento).



Agora a preparação da fonte de dados é: clicar nos campos obrigatórios na interface A1, marcar quais deles expandimos em linhas (se necessário) e opcionalmente definir o tipo de campos com antecedência.



Não carregamos o Tableau Serveratualização de fontes de dados volumosas - a atualização é feita por A1, e um hiper pronto é baixado para o servidor.



* Bônus - encorajamos os usuários a trabalhar dentro do A1. Se antes alguns usuários, após descarregar os relatórios brutos, os modificavam manualmente fora da ferramenta, agora, como todo o processo das etapas 1 a 3 ocorre em A1, é mais fácil para os usuários configurar o processo de limpeza ali.



Problema e solução



Um pouco sobre A1



Antes de começarmos a falar sobre nossa solução, precisamos falar sobre nosso produto interno A1, ao qual anexamos a geração de fontes de dados.



A1 é um produto interno da empresa, que se destina a simplificar o fluxo de trabalho dos funcionários cuja função principal é a seguinte:



  • Recupere dados de produtos de software MediaScope
  • Traga (limpe) esses dados em uma forma conveniente para analistas de assunto
  • Se necessário, prepare os dados para a criação de painéis (falaremos sobre isso hoje)


Depois que o usuário termina de limpar os dados, eles são armazenados no sistema A1. Em nossa terminologia, isso é chamado de "Container". Um contêiner é um documento normal no MongoDB, que precisamos transferir para o servidor Tableau.



O problema da equipe de BI



Nossa equipe de desenvolvimento de BI precisava, de alguma forma, obter dados de A1, que estavam armazenados no MongoDB, e construir painéis com base nos dados recebidos. Em primeiro lugar, tentamos obter dados do MongoDB usando ferramentas de placar regulares, mas isso não resolveu o problema:



  • Como os dados são armazenados no MongoDB, os dados com uma estrutura arbitrária são recebidos na entrada do placar, o que significa que você teria que manter essa lógica constantemente.
  • Para agregar dados do MongoDB, era necessário arrastar certos registros da coleção, e não a coleção inteira - o driver do Tableau não pode fazer isso.
  • Entre outras coisas, não era suficiente obter os dados: às vezes, eles tinham que ser "expandidos" - para "desfiar" algumas colunas em linhas. O que, também, não era tão fácil de fazer, de jeito nenhum.


O que nós descobrimos



Decidimos tentar resolver esse problema com minha bicicleta, usando a biblioteca Tableau Hyper API . Esta biblioteca permite que você crie um arquivo no formato .hyper, no qual é fácil adicionar dados e, em seguida, use-o como uma fonte de dados para criar um painel na placa do servidor.



Conforme os desenvolvedores do placar descrevem o hiper:

Hyper é um mecanismo de dados na memória de alto desempenho que ajuda os clientes a analisar rapidamente conjuntos de dados grandes ou complexos, avaliando com eficiência as consultas ao banco de dados. Com base na plataforma Tableau, o Hyper usa técnicas proprietárias de geração de código dinâmico e tecnologias de simultaneidade avançadas para obter alto desempenho em extrações e consultas.
O processo aproximado de trabalho em nosso programa é o seguinte:



  • O usuário seleciona contêineres e colunas desejadas
  • O sistema extrai dados de contêineres
  • Com base nos dados recebidos, o sistema determina os tipos de colunas
  • A criação de um hiper e a inserção de dados nele é inicializada
  • O hiper é carregado no servidor de placar
  • Os desenvolvedores de BI veem o hyper no servidor e criam um painel baseado nele


Quando novos dados forem colocados nos contêineres, o sistema receberá um sinal de que é necessário atualizar o hiper:



  • O sistema irá baixar o hyper do servidor de placar
  • Pegará dados novos do MongoDB e atualizará o hyper
  • Depois disso, o sistema carrega um novo hiper para o servidor, sobrescrevendo o existente.
  • O usuário só precisa clicar no botão "Atualizar" para exibir informações atualizadas no painel


O que o usuário vê



Conforme declarado anteriormente, A1 é um aplicativo da web. Usamos Vue.js e Vuetify para criar um serviço de hiper-geração front-end.



A interface do aplicativo é dividida em três telas.



Tela de seleção de recipientes



Na primeira tela, o usuário seleciona os containers e colunas desejados.



Se a opção "Unpivot" estiver habilitada, duas colunas adicionais serão criadas na hiper: variável - os nomes das colunas que são selecionadas pela coluna Metrics e os valores - os valores dessas colunas.



A coluna Dimensão adiciona uma coluna com a coluna selecionada de mesmo nome ao hiper. O número de colunas selecionadas Dimensões e seus nomes devem ser iguais em todos os contêineres para que a integridade da tabela no hiper não seja violada, portanto, existe uma coluna "Hiper nome", que permite especificar o nome da coluna selecionada, caso sejam nomeados de forma diferente nos contêineres.



Registros do processo de hipercriação



Isso conclui o processo de configuração do hiper. O usuário só precisa ir para a segunda tela, clicar em “Criar hiper” e observar o andamento dos eventos nos logs.



Configurações adicionais



A terceira tela contém configurações adicionais:



  • Você pode ignorar as atualizações se não precisarmos que o sistema atualize automaticamente o hiper
  • Você pode especificar um e-mail para enviar relatórios de atualização
  • Você pode especificar manualmente o tipo de dados para a coluna de valores (usado apenas no modo não dinâmico): float, string ou determinado automaticamente pelo sistema (falaremos mais sobre os tipos)
  • Você também pode especificar tipos de dados para colunas selecionadas em contêineres.


O que está sob o capô



A1 é escrito em Python. Para trabalhar com dados, usamos o Pandas e serializamos os dados dos pandas para conservar e armazená-los no MongoDB GridFS.



Quando um comando para criar um hiper é recebido, o sistema executa as seguintes operações:



  • Descarrega todos os contêineres necessários do MongoDB e desserializa os dados em datafremes do pandas
  • Prepara dados: deixa apenas as colunas necessárias nos dataframes, dá-lhes novos nomes, expande as tabelas se necessário via pandas.melt
  • Se o usuário tiver definido o tipo de dados para as colunas, converta os dados em float32 ou string
  • Após todo o trabalho preparatório com os dados, o sistema cria um arquivo via hyper api e envia o arquivo para o servidor de placar via tabcmd.


Vale a pena falar um pouco sobre os tipos de dados das colunas. Uma das características de armazenar dados em contêineres A1 é que os usuários não se preocupam com quais tipos atribuir às colunas, o pandas faz isso por eles perfeitamente: o sistema enfrenta calmamente situações em que números e valores de string estão presentes em uma coluna. Porém, o hiper não gosta disso: se você disser a ele que a coluna deve ser do tipo int, o sistema irá xingar ao tentar inserir qualquer coisa que não seja um inteiro. Portanto, decidiu-se usar apenas dois tipos de dados em hypers: string e float.



Então, descobrimos o princípio geral de trabalho, vamos falar sobre como trabalhar com o próprio hiper.



Criação de um arquivo .hyper



Para trabalhar com o Hyper API, você precisa instalar a biblioteca, você pode baixá-la do site oficial aqui . Existem também alguns bons exemplos de como trabalhar com esta ferramenta. Vamos indicar brevemente os pontos principais.



O próprio arquivo hyiper é uma espécie de banco de dados, algo que lembra o SQLite. Por meio da API, você pode acessar os dados usando a sintaxe SQL:



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


Como nosso sistema é escrito em Python, também usaremos a biblioteca para a linguagem correspondente. Ao criar o arquivo, devemos especificar o nome do esquema, nome da tabela e colunas com tipos. O nome do esquema e a tabela devem ser chamados de “Extrair”, pois é nesse esquema com a tabela que o Tableau Server escala para extrair os dados dos livros.



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)


Depois de criar a tabela, precisamos criar colunas e definir tipos. Como dissemos antes, nossos dados têm apenas dois tipos (float ou string), portanto, com base no tipo de colunas no dataframe, definimos isso para as colunas:



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)


Depois de criar a tabela, você pode inserir dados:



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


Aqui, percorremos o dataframe linha por linha e acumulamos a lista com os valores via inserter.add_row () . Na verdade, existe uma função add_rows () na api do hyper , que pega uma lista de listas e já insere os valores. Por que isso não foi feito? Para salvar a RAM: para fornecer uma lista de listas de valores do dataframe, você precisa pedir ao pandas para fazer values.tolist () . E quando você tem 150 milhões de linhas de dados, acaba sendo uma operação muito cara para a RAM, embora isso não afete o desempenho de forma alguma (em qualquer caso, não foi notado que devido à iteração iterativa ao longo das linhas, a velocidade de criação de um hiper diminuiu de alguma forma). Além disso, add_rows ()funciona como açúcar sintático: na verdade, pega uma lista de listas e adiciona dados iterativamente.



Isso conclui a criação do nosso hiper. Em seguida, precisamos publicá-lo no servidor.



Publicar um arquivo em um servidor Tableau



Para acessar o tableau-server, usaremos o utilitário tabcmd - este é um utilitário de console que permite que você se conecte ao servidor e execute funções administrativas - crie usuários, grupos, livros e muito mais.



Vamos executar o comando tabcmd por meio do subprocesso Python.Popen:



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}"


Passamos o seguinte comando e chaves para tabcmd:



  • publicar : carregue um arquivo para o servidor
  • -n (--name) : qual nome de arquivo estará no servidor
  • -o (--overwrite) : se houver um arquivo com este nome, então sobrescreve
  • -r “A1_test” (--project): ( )
  • -s (--server): tableau-
  • -u -p:
  • --no-certcheck: SSL-




Descobrimos como criar um novo hiper, mas o que fazer quando o hiper consiste em dez containers e um deles recebeu novos dados? Vamos atualizar o hiper.



Quando novos dados chegam ao contêiner, o sistema verifica se há algum hypers que usa esse contêiner. Se houver, a tarefa é atualizar o hiper.



Para entender quais dados de qual contêiner estão no hiper, o sistema também cria uma coluna container_id adicional ao criar o hiper. Com essa abordagem, a atualização se torna muito simples:



  • Pegamos o arquivo do servidor
  • Excluímos todas as linhas do hyper, onde container_id é igual ao container atualizado
  • Insira novas linhas
  • Faça upload do arquivo sobrescrito de volta para o servidor.


O processo de recuperação de um arquivo é ligeiramente diferente do processo de download. Em primeiro lugar, não pegaremos o arquivo .hyper do servidor, mas sim o arquivo .tdsx, que depois descompactaremos e abriremos o próprio .hyper.



Para pegar o arquivo, usamos 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}"


Aqui usamos o seguinte comando e chaves:



  • get : obtém um arquivo do servidor. Se o arquivo test.hyper estiver no servidor, você precisará consultar o arquivo test.tdsx e todos eles estão no diretório da fonte de dados (não consegui pesquisar no Google por que esse recurso do placar, se você sabe, compartilhe nos comentários)
  • -f (--filename) : caminho completo, incluindo nome e extensão do arquivo, onde salvar o arquivo


Após o download do arquivo, ele deve ser descompactado por meio de um arquivo zip:



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


Após a descompactação, o hiper estará no diretório ./Data/Extracts .



Agora que temos a versão atual do arquivo, podemos remover linhas desnecessárias dele:



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()


Bem, inserir e publicar um arquivo já foi descrito acima.



Conclusão



Qual é o resultado final? Tendo feito o trabalho de implementação da geração de hiper-arquivos e sua entrega automática ao tableau-server, reduzimos significativamente a carga na equipe de BI, ficou mais fácil atualizar os dados no painel e, principalmente, mais rápido. O próprio conhecimento da hyper api não foi doloroso, a documentação é bem escrita e a própria integração da tecnologia em nosso sistema foi fácil.



Agradecemos sua atenção! Se você tiver dúvidas ou comentários, por favor, deixe-os nos comentários.



O artigo foi escrito em conjunto com Vasily Lavrov (VasilyFromOpenSpace) — -



All Articles