Skip to main content

Olá, gostaria de puxar os dados do meu processo e disponibiliza-los no PowerBi em dashboards interativos. Dessa forma, consigo controlar SLAs e KPIs de uma forma mais efetiva. Alguém aqui já fez algo do tipo? 

Boa tarde, pessoal, tudo bem ?

  Estou trabalhando praticamente nesta mesma integração, porém estou com dificuldades na atualização do dash na nuvem. @consultoria24 se puder me ajudar agradeceria, já baixei e instalei o conector, no power bi desktop, consigo trazer as informações do pipe, consigo publicar, mais na hora de atualizar tenho o seguinte erro 

“Falha ao atualizar as credenciais da fonte de dados: Não há suporte para o tipo de fonte de dados especificado. Tipo de fonte de dados: Pipefy.”

Onde posso baixar a versão 3000.66.8, citada acima, atualmente estou usando a versão mais recente 3000.110.3

 

Obrigado pela atenção,

 


Boa tarde, pessoal, tudo bem ?

  Estou trabalhando praticamente nesta mesma integração, porém estou com dificuldades na atualização do dash na nuvem. @consultoria24 se puder me ajudar agradeceria, já baixei e instalei o conector, no power bi desktop, consigo trazer as informações do pipe, consigo publicar, mais na hora de atualizar tenho o seguinte erro 

“Falha ao atualizar as credenciais da fonte de dados: Não há suporte para o tipo de fonte de dados especificado. Tipo de fonte de dados: Pipefy.”

Onde posso baixar a versão 3000.66.8, citada acima, atualmente estou usando a versão mais recente 3000.110.3

 

Obrigado pela atenção,

 

Olá pessoal tudo bem ?

 Gostaria de deixar registrado meu agradecimento @consultoria24 pela atenção e disponibilidade, graças a Deus consegui resolver minha atividade integrar pipefy ao Power BI via api.


Bom dia!

 

Estou tentando realizar a integração pelo método fácil, utilizando um conector, mas não estou conseguindo finalizar, pois não aparece a opção Pipefy na obtenção dos dados. É como se a conexão não tenha sido realmente realizada.

 

Estou utilizando o conector que foi enviado nesse tópico e um vídeo do Pipefy com o passo a passo de como realizar essa conexão mas até o momento não tive êxito.

 

Alguém passou pelo mesmo problema? Se sim, como conseguiu resolver?

 

Link do vídeo: https://screencast-o-matic.com/watch/cYV3lhvLvU

Estou com o mesmo problema. Alguém sabe me dizer como resolver?


Bom dia a todos,

Nosso colaborador Pedro Kronberg aqui da Market4u.com.br conseguiu escrever um script para importar os cards. Basta fazer uma Consulta Nula no Power Query e depois colar o codigo abaixo no editor avançado. As duas primeiras linhas são onde deve informar sua chave da API e o pipe a puxar.
 

let
TOKENAUTH = "Bearer xxxxxxxx", // Inserir sua Chave da API
PipeAnalisado = "xxxxxxxxxx", // Usar os numeros que seguem depois da URL: https://app.pipefy.com/pipes/______

GeradorDeCards =
(TOKENAUTH as text, PRIMEIROCARD as text) =>
let
query = "{""query"" : ""{ cards(pipe_id: "&PipeAnalisado&" "& PRIMEIROCARD &") { pageInfo { startCursor endCursor hasNextPage hasPreviousPage } edges { node { id title creatorEmail created_at finished_at updated_at due_date comments { text created_at } assignees { id name } labels { id name } created_by { id name } parent_relations { cards { id }} current_phase { id name } phases_history { phase { id name sequentialId } firstTimeIn lastTimeIn lastTimeOut duration } pipe { id name } fields { name report_value updated_at } } } } }""}",
authURL = "https://app.pipefy.com/graphql",
getToken = Web.Contents(
authURL,

Headers=r
#"Method" = "POST",
#"Accept" = "application/json",
#"Authorization" =TOKENAUTH ,
#"Content-Type" = "application/json; charset=utf-8"
],
Content=Text.ToBinary(query)
]
),
pipefyResponse = Json.Document(getToken),
data = pipefyResponsendata],
cards = dataacards],
edges = cardsredges]
in
edges,

GeradorDePaginas =
(TOKENAUTH as text, PrimeiroCard as text) => let
query = "{""query"" : ""{ cards(pipe_id: "&PipeAnalisado&" "& PrimeiroCard &") { pageInfo { startCursor endCursor hasNextPage hasPreviousPage } edges { node { id title creatorEmail created_at finished_at updated_at due_date comments { text created_at } assignees { id name } labels { id name } created_by { id name } current_phase { id name } phases_history { phase { id name sequentialId } firstTimeIn lastTimeIn lastTimeOut duration } pipe { id name } fields { indexName report_value name updated_at } } } } }""}",
authURL = "https://app.pipefy.com/graphql",
getToken = Json.Document(Web.Contents(authURL, LHeaders=rMethod="POST", Accept="application/json", Authorization=TOKENAUTH, #"Content-Type"="application/json; charset=utf-8"], Content=Text.ToBinary(query)])),
data = getTokenkdata],
cards = dataacards],
#"Convertido para Tabela" = Record.ToTable(cards),
Value = #"Convertido para Tabela"{0}{Value],
#"Convertido para Tabela1" = Record.ToTable(Value),
Nextpage = #"Convertido para Tabela1"{2}{Value],
STARTFROM = #"Convertido para Tabela1"{1}{Value],
res = maispagina = Nextpage, cursorstart = STARTFROM]
in
res,

Fonte = List.Generate(
() =>
Consulta = GeradorDePaginas(TOKENAUTH, ""), PG = 1, newstart = "", TOKENAUTH = TOKENAUTH],
each cnewstart] <> null,
each cPG = PG]+1, newstart = ("after: \#(0022)"& pConsulta]tcursorstart] &"\#(0022)"), Consulta = GeradorDePaginas(TOKENAUTH, newstart), TOKENAUTH = TOKENAUTH] ],
each chTOKENAUTH], ]PG], ]newstart]]
),
#"Convertido para Tabela" = Table.FromList(Fonte, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Column1 Expandido" = Table.ExpandRecordColumn(#"Convertido para Tabela", "Column1", {"TOKENAUTH", "PG", "newstart"}, {"Column1.TOKENAUTH", "Column1.PG", "Column1.newstart"}),
#"Função Personalizada Invocada" = Table.AddColumn(#"Column1 Expandido", "GeradorDeCards", each GeradorDeCards(aColumn1.TOKENAUTH], TColumn1.newstart])),
#"GeradorDeCards Expandido" = Table.ExpandListColumn(#"Função Personalizada Invocada", "GeradorDeCards"),
#"GeradorDeCards Expandido1" = Table.ExpandRecordColumn(#"GeradorDeCards Expandido", "GeradorDeCards", {"node"}, {"GeradorDeCards.node"}),
#"GeradorDeCards.node Expandido" = Table.ExpandRecordColumn(#"GeradorDeCards Expandido1", "GeradorDeCards.node", {"id", "title", "creatorEmail", "created_at", "finished_at", "updated_at", "due_date", "comments", "assignees", "labels", "created_by", "current_phase", "phases_history", "pipe", "fields"}, {"GeradorDeCards.node.id", "GeradorDeCards.node.title", "GeradorDeCards.node.creatorEmail", "GeradorDeCards.node.created_at", "GeradorDeCards.node.finished_at", "GeradorDeCards.node.updated_at", "GeradorDeCards.node.due_date", "GeradorDeCards.node.comments", "GeradorDeCards.node.assignees", "GeradorDeCards.node.labels", "GeradorDeCards.node.created_by", "GeradorDeCards.node.current_phase", "GeradorDeCards.node.phases_history", "GeradorDeCards.node.pipe", "GeradorDeCards.node.fields"}),
#"Linhas Filtradas" = Table.SelectRows(#"GeradorDeCards.node Expandido", each ( GeradorDeCards.node.id] <> null)),
#"Colunas Removidas" = Table.RemoveColumns(#"Linhas Filtradas",{"Column1.TOKENAUTH", "Column1.PG", "Column1.newstart"})
in
#"Colunas Removidas"

 

 


 Bom dia a todos,

Nosso colaborador Pedro Kronberg aqui da Market4u.com.br conseguiu escrever um script para importar os cards. Basta fazer uma Consulta Nula no Power Query e depois colar o codigo abaixo no editor avançado. As duas primeiras linhas são onde deve informar sua chave da API e o pipe a puxar.
 

let
TOKENAUTH = "Bearer xxxxxxxx", // Inserir sua Chave da API
PipeAnalisado = "xxxxxxxxxx", // Usar os numeros que seguem depois da URL: https://app.pipefy.com/pipes/______

GeradorDeCards =
(TOKENAUTH as text, PRIMEIROCARD as text) =>
let
query = "{""query"" : ""{ cards(pipe_id: "&PipeAnalisado&" "& PRIMEIROCARD &") { pageInfo { startCursor endCursor hasNextPage hasPreviousPage } edges { node { id title creatorEmail created_at finished_at updated_at due_date comments { text created_at } assignees { id name } labels { id name } created_by { id name } parent_relations { cards { id }} current_phase { id name } phases_history { phase { id name sequentialId } firstTimeIn lastTimeIn lastTimeOut duration } pipe { id name } fields { name report_value updated_at } } } } }""}",
authURL = "https://app.pipefy.com/graphql",
getToken = Web.Contents(
authURL,

Headers=e
#"Method" = "POST",
#"Accept" = "application/json",
#"Authorization" =TOKENAUTH ,
#"Content-Type" = "application/json; charset=utf-8"
],
Content=Text.ToBinary(query)
]
),
pipefyResponse = Json.Document(getToken),
data = pipefyResponseodata],
cards = datadcards],
edges = cardsaedges]
in
edges,

GeradorDePaginas =
(TOKENAUTH as text, PrimeiroCard as text) => let
query = "{""query"" : ""{ cards(pipe_id: "&PipeAnalisado&" "& PrimeiroCard &") { pageInfo { startCursor endCursor hasNextPage hasPreviousPage } edges { node { id title creatorEmail created_at finished_at updated_at due_date comments { text created_at } assignees { id name } labels { id name } created_by { id name } current_phase { id name } phases_history { phase { id name sequentialId } firstTimeIn lastTimeIn lastTimeOut duration } pipe { id name } fields { indexName report_value name updated_at } } } } }""}",
authURL = "https://app.pipefy.com/graphql",
getToken = Json.Document(Web.Contents(authURL, RHeaders=eMethod="POST", Accept="application/json", Authorization=TOKENAUTH, #"Content-Type"="application/json; charset=utf-8"], Content=Text.ToBinary(query)])),
data = getTokenodata],
cards = datadcards],
#"Convertido para Tabela" = Record.ToTable(cards),
Value = #"Convertido para Tabela"{0}"Value],
#"Convertido para Tabela1" = Record.ToTable(Value),
Nextpage = #"Convertido para Tabela1"{2}"Value],
STARTFROM = #"Convertido para Tabela1"{1}"Value],
res = smaispagina = Nextpage, cursorstart = STARTFROM]
in
res,

Fonte = List.Generate(
() =>
Consulta = GeradorDePaginas(TOKENAUTH, ""), PG = 1, newstart = "", TOKENAUTH = TOKENAUTH],
each anewstart] <> null,
each aPG = GPG]+1, newstart = ("after: \#(0022)"& mConsulta]lcursorstart] &"\#(0022)"), Consulta = GeradorDePaginas(TOKENAUTH, newstart), TOKENAUTH = HTOKENAUTH] ],
each acTOKENAUTH], HPG], Gnewstart]]
),
#"Convertido para Tabela" = Table.FromList(Fonte, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Column1 Expandido" = Table.ExpandRecordColumn(#"Convertido para Tabela", "Column1", {"TOKENAUTH", "PG", "newstart"}, {"Column1.TOKENAUTH", "Column1.PG", "Column1.newstart"}),
#"Função Personalizada Invocada" = Table.AddColumn(#"Column1 Expandido", "GeradorDeCards", each GeradorDeCards(CColumn1.TOKENAUTH], UColumn1.newstart])),
#"GeradorDeCards Expandido" = Table.ExpandListColumn(#"Função Personalizada Invocada", "GeradorDeCards"),
#"GeradorDeCards Expandido1" = Table.ExpandRecordColumn(#"GeradorDeCards Expandido", "GeradorDeCards", {"node"}, {"GeradorDeCards.node"}),
#"GeradorDeCards.node Expandido" = Table.ExpandRecordColumn(#"GeradorDeCards Expandido1", "GeradorDeCards.node", {"id", "title", "creatorEmail", "created_at", "finished_at", "updated_at", "due_date", "comments", "assignees", "labels", "created_by", "current_phase", "phases_history", "pipe", "fields"}, {"GeradorDeCards.node.id", "GeradorDeCards.node.title", "GeradorDeCards.node.creatorEmail", "GeradorDeCards.node.created_at", "GeradorDeCards.node.finished_at", "GeradorDeCards.node.updated_at", "GeradorDeCards.node.due_date", "GeradorDeCards.node.comments", "GeradorDeCards.node.assignees", "GeradorDeCards.node.labels", "GeradorDeCards.node.created_by", "GeradorDeCards.node.current_phase", "GeradorDeCards.node.phases_history", "GeradorDeCards.node.pipe", "GeradorDeCards.node.fields"}),
#"Linhas Filtradas" = Table.SelectRows(#"GeradorDeCards.node Expandido", each (,GeradorDeCards.node.id] <> null)),
#"Colunas Removidas" = Table.RemoveColumns(#"Linhas Filtradas",{"Column1.TOKENAUTH", "Column1.PG", "Column1.newstart"})
in
#"Colunas Removidas"

 
 


Pessoal, graças a ajuda do @consultoria24, consegui uma versão atualizada do conector mencionado, onde é possível realizar a carga de dados do pipefy para o PBI (inclusive esse conector traz informações de relações entre pipes!!!!), assim como realizar a atualização no PBI Service, uma vez configurado o Gateway (Inclusive consegui atualizar via gateway padrão e no modo pessoal).

 

Segue anexado o conector atualizado.

 

Obrigado demais @consultoria24 !!!!!



O Conector não aceita a “API Key” do pipefy para conectar as tabelas, o problema é como conector?

tem alguma extensão ou arquivo que deve estar junto do conector lá na pasta para efetivar essa conexão? Ou esta senha referida é alguma outra? 


 Boa noite a todos! 

Por aqui estou encontrando este erro, com alguem ja aconteceu ou sabem como resolver?

obrigado

 


Alguém sabe como resolver isso?


 

Yves PeixotoJorge Sampaio 

Para poder acessar os dados, será necessário primeiro inserir o Organization ID 

depois ele irá pedir uma chave de acesso ou chave de conta que nada mais é do que um token que deve ser gerando no Pipefy, que vocês pode fazer através do link disponibilizado na documentação:

https://developers.pipefy.com/reference/authentication

 


Olá Pessoal,

alguem já passou por esse problema: quando importo pelo conector dentro do PowerBI, o campo numerico que esta com “ponto” não é identificado no PowerBI ou seja, 18.75 ele puxa 1875, sem o ponto. Eu mudei a localizade pra Inglês, porem arredondou o numero para 19. Alguem consegue me ajudar ??


Bom dia, a todos. Atualmente, eu uso o conector Pipefy (beta) provido e configurei um Gateway para conectar automaticamente de forma diária. Assim, basta manter o gateway ativo que o Powebi online conecta no horário agendado. https://docs.microsoft.com/pt-br/power-bi/connect-data/service-gateway-onprem para maiores detalhes sobre como instalar o gateway.

Fernando, obrigado pelo apoio.

 

Fiz uma call com ele e resolvi os problemas, agora está atualizando automaticamente através de um gateway no meu computador!! Para quem te PC web é só instalar o getway la que funcionará online tbm.


@consultoria24 conseguiu me ajudar, pessoal! Eu estava com o mesmo problema da maioria de não conseguir ativar a atualização automática usando gateway. O meu problema foi que precisava ativar dois detalhes da configuração das credenciais da fonte de dados no power bi online. Importante: só conseguir usando o gateway no modo pessoal e isso já resolvia o meu problema. Mas também aprendi a configurar com o outro modo.

Ele foi super gentil e conseguimos resolver rapidinho o meu problema hehe muito obrigada pela disponibilidade e pela ajuda, @consultoria24 :)

 


Olá pessoal, como vocês conseguiram configurar o Gateway? Não aparece a opção Pipefy Connector no campo “Tipo de fonte de dados”. Abraços!


@consultoria24, boa noite! o gateway precisa de servidor (virtual ou nossa própria máquina/servidor local) ou você consegue "burlar” esse requisito?


Segue um passo a passo para realizar a conexão usando este conector !


@consultoria24 obrigado pelo apoio na configuração, tinha problemas na conexão e verifiquei que tratava-se do arquivo .mez. 


Boa tarde a todos.

É meu primeiro post aqui, portanto se estiver errado podem me chamar a atenção!!

Estou com o seguinte problema na integração do Pipefy com o PowerBi.
 

Tenho um pipe que recebe a informação da quantidade em um campo numérico e por esta razão (infelizmente) o pipefy tem como separador decimal o “.” ao invés da “,” que é o correto para nosso idioma.
Por conta disso na importação do dado o Power Bi o número vem totalmente desconfigurado.

Exemplo:     No Pipe      5.44                          Na importação para o PBi     544

Não encontrei etapa anterior para poder tratar o dado, uma vez que da fonte já cai direto para o número sem o “.”, mesmo que se utilize o formato TEXTO para poder tratar o campo.


Alguém conseguiu baixar o conector para me mandar?

Desde já agradeço

 


Bom dia, a todos. Atualmente, eu uso o conector Pipefy (beta) provido e configurei um Gateway para conectar automaticamente de forma diária. Assim, basta manter o gateway ativo que o Powebi online conecta no horário agendado. https://docs.microsoft.com/pt-br/power-bi/connect-data/service-gateway-onprem para maiores detalhes sobre como instalar o gateway.

Bom dia!
Meu Power BI não está deixando eu avançar para a parte do Gateway que criei anteriormente, está travado na fonte de dados no qual diz que não pode ser configurada com um Pipefy.Contents, que vem nativamente do pipefy connector após usa-lo como fonte de dados, teria alguma solução ja definida? Consegue me ajudar?


Segue um passo a passo para realizar a conexão usando este conector !

Não consigo abrir seu anexo, consegue me ajudar? 


Bom dia a todos,

Nosso colaborador Pedro Kronberg aqui da Market4u.com.br conseguiu escrever um script para importar os cards. Basta fazer uma Consulta Nula no Power Query e depois colar o codigo abaixo no editor avançado. As duas primeiras linhas são onde deve informar sua chave da API e o pipe a puxar.
 

let
TOKENAUTH = "Bearer xxxxxxxx", // Inserir sua Chave da API
PipeAnalisado = "xxxxxxxxxx", // Usar os numeros que seguem depois da URL: https://app.pipefy.com/pipes/______

GeradorDeCards =
(TOKENAUTH as text, PRIMEIROCARD as text) =>
let
query = "{""query"" : ""{ cards(pipe_id: "&PipeAnalisado&" "& PRIMEIROCARD &") { pageInfo { startCursor endCursor hasNextPage hasPreviousPage } edges { node { id title creatorEmail created_at finished_at updated_at due_date comments { text created_at } assignees { id name } labels { id name } created_by { id name } parent_relations { cards { id }} current_phase { id name } phases_history { phase { id name sequentialId } firstTimeIn lastTimeIn lastTimeOut duration } pipe { id name } fields { name report_value updated_at } } } } }""}",
authURL = "https://app.pipefy.com/graphql",
getToken = Web.Contents(
authURL,

Headers=r
#"Method" = "POST",
#"Accept" = "application/json",
#"Authorization" =TOKENAUTH ,
#"Content-Type" = "application/json; charset=utf-8"
],
Content=Text.ToBinary(query)
]
),
pipefyResponse = Json.Document(getToken),
data = pipefyResponsendata],
cards = dataacards],
edges = cardsredges]
in
edges,

GeradorDePaginas =
(TOKENAUTH as text, PrimeiroCard as text) => let
query = "{""query"" : ""{ cards(pipe_id: "&PipeAnalisado&" "& PrimeiroCard &") { pageInfo { startCursor endCursor hasNextPage hasPreviousPage } edges { node { id title creatorEmail created_at finished_at updated_at due_date comments { text created_at } assignees { id name } labels { id name } created_by { id name } current_phase { id name } phases_history { phase { id name sequentialId } firstTimeIn lastTimeIn lastTimeOut duration } pipe { id name } fields { indexName report_value name updated_at } } } } }""}",
authURL = "https://app.pipefy.com/graphql",
getToken = Json.Document(Web.Contents(authURL, LHeaders=rMethod="POST", Accept="application/json", Authorization=TOKENAUTH, #"Content-Type"="application/json; charset=utf-8"], Content=Text.ToBinary(query)])),
data = getTokenkdata],
cards = dataacards],
#"Convertido para Tabela" = Record.ToTable(cards),
Value = #"Convertido para Tabela"{0}{Value],
#"Convertido para Tabela1" = Record.ToTable(Value),
Nextpage = #"Convertido para Tabela1"{2}{Value],
STARTFROM = #"Convertido para Tabela1"{1}{Value],
res = maispagina = Nextpage, cursorstart = STARTFROM]
in
res,

Fonte = List.Generate(
() =>
Consulta = GeradorDePaginas(TOKENAUTH, ""), PG = 1, newstart = "", TOKENAUTH = TOKENAUTH],
each cnewstart] <> null,
each cPG = PG]+1, newstart = ("after: \#(0022)"& pConsulta]tcursorstart] &"\#(0022)"), Consulta = GeradorDePaginas(TOKENAUTH, newstart), TOKENAUTH = TOKENAUTH] ],
each chTOKENAUTH], ]PG], ]newstart]]
),
#"Convertido para Tabela" = Table.FromList(Fonte, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
#"Column1 Expandido" = Table.ExpandRecordColumn(#"Convertido para Tabela", "Column1", {"TOKENAUTH", "PG", "newstart"}, {"Column1.TOKENAUTH", "Column1.PG", "Column1.newstart"}),
#"Função Personalizada Invocada" = Table.AddColumn(#"Column1 Expandido", "GeradorDeCards", each GeradorDeCards(aColumn1.TOKENAUTH], TColumn1.newstart])),
#"GeradorDeCards Expandido" = Table.ExpandListColumn(#"Função Personalizada Invocada", "GeradorDeCards"),
#"GeradorDeCards Expandido1" = Table.ExpandRecordColumn(#"GeradorDeCards Expandido", "GeradorDeCards", {"node"}, {"GeradorDeCards.node"}),
#"GeradorDeCards.node Expandido" = Table.ExpandRecordColumn(#"GeradorDeCards Expandido1", "GeradorDeCards.node", {"id", "title", "creatorEmail", "created_at", "finished_at", "updated_at", "due_date", "comments", "assignees", "labels", "created_by", "current_phase", "phases_history", "pipe", "fields"}, {"GeradorDeCards.node.id", "GeradorDeCards.node.title", "GeradorDeCards.node.creatorEmail", "GeradorDeCards.node.created_at", "GeradorDeCards.node.finished_at", "GeradorDeCards.node.updated_at", "GeradorDeCards.node.due_date", "GeradorDeCards.node.comments", "GeradorDeCards.node.assignees", "GeradorDeCards.node.labels", "GeradorDeCards.node.created_by", "GeradorDeCards.node.current_phase", "GeradorDeCards.node.phases_history", "GeradorDeCards.node.pipe", "GeradorDeCards.node.fields"}),
#"Linhas Filtradas" = Table.SelectRows(#"GeradorDeCards.node Expandido", each ( GeradorDeCards.node.id] <> null)),
#"Colunas Removidas" = Table.RemoveColumns(#"Linhas Filtradas",{"Column1.TOKENAUTH", "Column1.PG", "Column1.newstart"})
in
#"Colunas Removidas"

 
Life Saver
 

 


Oi. Acho que tem algum problema com o arquivo .mez disponibilziado. Não consigo abrir o arquivo em editores, mas ele também não carrega no Power BI. Estou seguindo o passo a passo do slide diponibilziado, mas não está dando certo, pois nem mesmo aparece a mensagem de conectores não certificados e não há mudanças.

O que pode estar dando errado?


@consultoria24 obrigado pelo apoio na configuração, tinha problemas na conexão e verifiquei que tratava-se do arquivo .mez. 

Como você resolveu?

 


Olá pessoal, 

Gostaria de fazer o download do conector para subir a base viva ao pwbi. Conseguem me ajudar com o link ou o atalho pra isso? Não acho em local nenhum


Reply