Adição de item com anexo por API

Gostaria de uma direção sobre como funciona a inserção de anexos nos itens de coleção por meio da API.

Estou num projeto que visa o cadastro dos itens de outro sistema, cujo conteúdo deve ser integrado ao Tainacan por API

Olá @Andre_Filipe_Aloise , tudo bem? Seja bem vindo a nossa comunidade!

Os anexos precisam ser instanciados antes pelo endpoint de media do WordPress:

Note que há um atributo “post”, onde você irá passar o ID do seu item. Uma boa maneira de entender como essas coisas funcionam é abrindo o painel de desenvolvedores do seu navegador e olhando as operações Fetch/XHR na aba Rede/Network. O admin do Tainacan é todo feito com requisições para a API do WordPress e do próprio Tainacan, cuja documentação está disponível aqui:

Nos diga se isso ajuda!

Lembrei aqui que no contexto da edição do item o upload é feito pela galeria de mídias e ela realiza uma requisição ajax ao invés do REST, portanto pode ser menos claro para o que você quer fazer. Um exemplo mais claro você vai encontrar inspecionando o que rola na tela de “Adição em massa de itens”. Vá para a lista de itens de uma coleção e clique no botão “Adicionar itens em massa”:
image

Lá, envie alguns arquivos com o inspetor de rede aberto. Você vai poder observar justamente o fluxo que você está querendo todo feito pela API (criar item, criar anexo, associar anexo ao item…)

Acho que consegui entender mais ou menos a lógica, é parecido com upload em massa por CSV.

Só que está dando erro aqui. Não dá muitos detalhes. Tem algum log onde as tentativas de upload em massa fiquem registradas? Nosso ambiente permite upload de arquivos de 10GB, mas quando tento pelo Tainacan não consigo enviar arquivos de 1 MB

Há logs quando a operação ocorre em plano de fundo (como é o caso dos importadores), mas não creio que seria seu caso, você está fazendo um script né? Pode compartilhar seu código? Ou mostrar as informações de erro que ele está gerando pra tentarmos investigar?

Ainda não cheguei na etapa de script. Foi um envio em massa mesmo, pela interface

Mas olhando, por exemplo, pro console na aba de redes, você consegue nos apontar quais requisições que falharam? Mais sobre isso aqui:

Hoje fiz um script. Consigo enviar um arquivo para a biblioteca do WordPress, mas não consigo associá-lo como documento de um item pré-existente.

O código não acusa erros. Em contrapartida nada muda no item. Essa associação que está sendo meu desafio.

Você está enviando o arquivo usando o enpoint da wp-media? Se sim, não se esqueça de passar o ID do Item no campo post quando for criar a mídia. Assim você diz que aquela imagem está anexada ao Item. Feito isso, pegue o ID da media retornada e faça um PATCH/PUT no item passando ID da mídia no campo document. Aproveite também para setar o document_type como attachment.

Essas duas consultas devem fazer a amarração da mídia com o item.

Esse é o código do script. Não sei se tem algo faltando:

import requests
import json
from requests.auth import HTTPBasicAuth

# Dados de autenticação e URLs da API do Wordpress
wordpress_url = 'https://meusite.com.br/wp-json/'
username = 'usuario'
password = 'senha'

# Autenticação
auth = HTTPBasicAuth(username, password)

def enviar_documento(item_id, arquivo_path):
    # Endpoint para enviar documento como anexo para o item
    endpoint = f'{wordpress_url}wp/v2/media'

    # Abre o arquivo para enviar
    with open(arquivo_path, 'rb') as arquivo:
        files = {
            'file': (arquivo.name, arquivo, 'application/pdf')  # Ajuste o tipo MIME conforme necessário
        }

        # Fazendo a requisição POST para enviar o documento como anexo
        response = requests.post(endpoint, auth=auth, files=files)

        # Verificando o status da requisição
        if response.status_code == 201:
            # Documento enviado com sucesso, recuperar o ID do anexo
            attachment_id = response.json()['id']
            #attachment_id = response.json()['guid']['raw']
            print(response.json()['guid']['raw'])
            return attachment_id
        else:
            print(f'Falha ao enviar documento: {response.status_code}')
            print(response.text)
            return None

def atualizar_item_com_documento(collection_id, item_id, attachment_id):
    # Endpoint para atualizar metadados do item
    endpoint = f"{wordpress_url}tainacan/v2/items/{item_id}"

    # Corpo da requisição para atualizar os metadados do item
    corpo = {
        'metadata': {
            #'document': attachment_id,  # Chave 'document' deve corresponder ao campo de documento na configuração do Tainacan
            #'document_type': 'attachment',
            #'document_mimetype': 'application/pdf',
            #'document': attachment_id
            'attachment': {
                'attachment_id': attachment_id,
                'document_type': 'attachment'  # Conforme especificado pela API do Tainacan
            }
        }
    }

    # Fazendo a requisição PUT para atualizar o item
    headers = {"content-type": "application/json"}
    response = requests.put(endpoint, data=json.dumps(corpo), headers=headers, auth=auth)

    # Verificando o status da requisição
    if response.status_code == 200:
        print(response.json())
        print(f'Item atualizado com sucesso!')
    else:
        print(f'Falha ao atualizar item: {response.status_code}')
        print(response.text)

def main():
    collection_id = '35'
    item_id = '12005'
    arquivo_path = 'acordao-tcu-levantamento-spu.pdf'

    # Enviar documento como anexo e obter o ID do anexo
    attachment_id = enviar_documento(item_id, arquivo_path)

    if attachment_id:
        # Atualizar os metadados do item com o ID do anexo enviado
        atualizar_item_com_documento(collection_id, item_id, attachment_id)

if __name__ == '__main__':
    main()

Olhando por alto tem duas questões que me parecem estranhas:

  1. Você até passa o item_id pra função que envia o documento mas não usa ele. No caso ele deve ser passado com parâmetro post pra wp/v2/media. Acredito que pode ser passado tanto no body como via query.
  2. Não entendi bem o porquê do objeto metadata lá embaixo quando você vai atualizar o item. O document é um atributo direto do item, você passa ele como parâmetro:
    ReDoc Interactive Demo

Acho que agora deu certo. @mateus.m.luna dá uma olhada se esse código faz sentido pra vc:

import requests
import json
from requests.auth import HTTPBasicAuth

# Dados de autenticação e URLs da API do Wordpress
wordpress_url = 'https://memoria-spu.nuvem.gov.br/wp-json/'
username = 'username'
password = 'password'

# Autenticação
auth = HTTPBasicAuth(username, password)

def enviar_documento(item_id, arquivo_path):
    # Endpoint para enviar documento como anexo para o item
    endpoint = f'{wordpress_url}wp/v2/media'

    # Abre o arquivo para enviar
    with open(arquivo_path, 'rb') as arquivo:
        files = {
            'file': (arquivo.name, arquivo, 'application/pdf'),  # Ajuste o tipo MIME conforme necessário
            'post': item_id
        }

        # Fazendo a requisição POST para enviar o documento como anexo
        response = requests.post(endpoint, auth=auth, files=files)

        # Verificando o status da requisição
        if response.status_code == 201:
            # Documento enviado com sucesso, recuperar o ID do anexo
            attachment_id = response.json()['id']
            #attachment_id = response.json()['guid']['raw']
            print(response.json()['guid']['raw'])
            return attachment_id
        else:
            print(f'Falha ao enviar documento: {response.status_code}')
            print(response.text)
            return None

def atualizar_item_com_documento(collection_id, item_id, attachment_id):
    # Endpoint para atualizar metadados do item
    endpoint = f"{wordpress_url}tainacan/v2/items/{item_id}"

    # Corpo da requisição para atualizar os metadados do item
    corpo = {
        'document': str(attachment_id),  # Chave 'document' deve corresponder ao campo de documento na configuração do Tainacan
        'document_type': 'attachment'
    }

    # Fazendo a requisição PUT para atualizar o item
    headers = {"content-type": "application/json"}
    response = requests.put(endpoint, data=json.dumps(corpo), headers=headers, auth=auth)

    # Verificando o status da requisição
    if response.status_code == 200:        
        print(f'Item atualizado com sucesso!')
    else:
        print(f'Falha ao atualizar item: {response.status_code}')
        print(response.text)

def main():
    collection_id = '35'
    item_id = '12005'
    arquivo_path = 'vantagem_pdf.pdf'

    # Enviar documento como anexo e obter o ID do anexo
    attachment_id = enviar_documento(item_id, arquivo_path)

    if attachment_id:
        # Atualizar os metadados do item com o ID do anexo enviado
        atualizar_item_com_documento(collection_id, item_id, attachment_id)

if __name__ == '__main__':
    main()
``
1 curtida

Sim, me parece fazer sentido @Andre_Filipe_Aloise (editei o seu comentário pra tirar a senha, pra evitar que vcs recebam ataques).

Eu nunca fiz isso via script python, mas toda a interface administrativa do Tainacan é feita via este tipo de requisição REST, com JavaScript. Então tecnicamente, tudo o que rola lá dá pra fazer via scripts, só é complexo mesmo porque tem etapas. Por exemplo, pra se atualizar um item, não se envia diretamente os dados dos metadados dentro de um campo e sim via item/:id/metadata/:id-metadata. É o tipo de detalhe que demanda um pouco de estudo dessa documentação ou uma análise das próprias requisições que ocorrem no navegador, como sugeri antes. Mas aos poucos você vai pegando jeito!

Este tópico foi fechado automaticamente 60 dias depois da última resposta. Novas respostas não são mais permitidas.