Adicionar gráficos ao Noção

Muitos não possuem gráficos no Noion. Decidi arquivar uma coisa automática para gerá-los.

É assim que parece da minha parte:

imagem
Qualquer pessoa interessada em como isso é implementado, por favor, em cat.

Parte 1. Declaração do problema


O problema, de fato, é que não há gráficos no Noion e as informações do tablet não podem ser visualizadas exatamente assim (mas eu quero). Portanto, você precisa criar algo que:

  1. Levará uma lista de páginas onde os gráficos poderiam ser potencialmente
  2. Coletará dessas páginas uma descrição do cronograma. As descrições devem estar nas páginas devido à compreensibilidade do leitor de páginas +, para não entrar no código com muita frequência para corrigi-lo.
  3. Adicionará um gráfico imediatamente após sua descrição, excluindo a versão anterior do gráfico.
  4. Isso será feito automaticamente (uma vez por hora) e, de preferência, de forma gratuita.

Parte 2. Colocando nas prateleiras


Vamos escrever um servidor no Django. É o Django, porque a lib não oficial da API do Notion é escrita em python. Em seguida, enviamos a coisa toda para o Heroku. A partir do IFTTT, vamos usar o Heroku com certa frequência.

imagem

Parte 3. Entendendo o que precisamos escrever


  1. Método para responder a uma solicitação do IFTTT
  2. Método para vasculhar as páginas de noção e procurar descrições de gráficos
  3. Método para recuperar dados para um gráfico de uma tabela
  4. Método para desenhar um gráfico e adicioná-lo a uma página

Parte 4. Escrevendo um Código


Vá para Noção, pressione Ctrl + Shift + J, vá para Aplicativo -> Cookies, copie o token_v2 e chame-o de TOKEN. Isso é necessário para que a biblioteca possa interagir de alguma forma com a API do Notion.

De alguma forma, precisamos armazenar uma lista de páginas em que uma descrição para gráficos possa ser encontrada. Manteremos este negócio simples:

PAGES = [ "https://www.notion.so/mixedself/Dashboard-40a3156030fd4d9cb1935993e1f2c7eb" ] 

Para analisar de alguma forma a descrição em si, precisamos de palavras-chave para:

  1. Campos cujos dados estarão no eixo X
  2. Campos cujos dados estarão no eixo Y
  3. URL no prato

Ficará assim:

 BASE_KEY = "Base:" X_AXIS_KEY = "X axis:" Y_AXIS_KEY = "Y axis:" 

Ou seja, uma descrição vazia do gráfico será mais ou menos assim:

 def get_empty_object(): return { "database": "", "x": "", "y": "" } 

Também precisamos verificar se a descrição está vazia. Arquivaremos uma função especial para isso. Se todos os campos não estiverem vazios, o objeto estará completo e podemos começar a desenhar o gráfico.

 def is_not_empty(thing): return thing != "" def check_for_completeness(object): return is_not_empty(object["database"]) and is_not_empty(object["x"]) and is_not_empty(object["y"]) 

Dados (na verdade, é apenas um texto) para gerar uma descrição, você precisa limpá-la de alguma forma. Vamos escrever algumas funções para isso. Breve explicação: A noção armazena negrito (como na figura acima do corte) __ aqui.

 def br_text(text): return "__" + text + "__" def clear_text(text): return text.replace(br_text(BASE_KEY), "").replace(BASE_KEY, "") \ .replace(br_text(X_AXIS_KEY), "").replace(X_AXIS_KEY, "") \ .replace(br_text(Y_AXIS_KEY), "").replace(Y_AXIS_KEY, "").strip() 

Agora vamos escrever, talvez, a principal função da nossa coisinha. Sob o código, há uma explicação do que está acontecendo aqui:

 def plot(): client = NotionClient(token_v2=TOKEN) for page in PAGES: blocks = client.get_block(page) thing = get_empty_object() for i in range(len(blocks.children)): block = blocks.children[i] print(block.type) if block.type != "image": title = block.title if BASE_KEY in title: thing["database"] = clear_text(title).split("](")[0].replace("[", "") elif X_AXIS_KEY in title: thing["x"] = clear_text(title) elif Y_AXIS_KEY in title: thing["y"] = clear_text(title) if check_for_completeness(thing): # not last block if i != len(blocks.children) - 1: next_block = blocks.children[i + 1] # if next block is picture, then it is previous # version of the plot, then we should remove it if blocks.children[i + 1].type == "image": next_block.remove() draw_plot(client, thing, block, blocks) thing = get_empty_object() 

Conectamos nossa biblioteca ao Noção. Depois, percorremos uma série de páginas, nas quais podemos potencialmente precisar de gráficos. Verificamos todas as linhas da página: existe uma de nossas chaves ou não. Se de repente houver - limpamos o texto de lá e o colocamos no objeto. Assim que o objeto estiver cheio, verificamos se o gráfico gerado já existe (se houver, e o excluímos) e desenhamos um novo gráfico.

Em seguida, escreveremos uma função para coletar dados da placa.

 def get_lines_array(thing, client): database = client.get_collection_view(thing["database"]) rows = database.default_query().execute() lines_array = [] for i in range(1, len(rows)): previous_row = rows[i - 1] current_row = rows[i] line = [(get_point_from_row(thing, previous_row)), (get_point_from_row(thing, current_row))] lines_array.append(line) return lines_array 

Aqui chegamos à base, obtemos todas as suas linhas e passamos por todas as linhas, formando um conjunto de linhas de ponto a ponto.

O que é get_point_from_row? O fato é que, se nosso objeto é uma data (geralmente você só precisa exibir a data no eixo X), ele simplesmente não pode ser exibido e é necessário processá-lo ainda mais:

 def get_point_from_row(thing, row): x_property = row.get_property(thing["x"]) y_property = row.get_property(thing["y"]) if thing["x"] == "date": x_property = x_property.start if thing["y"] == "date": y_property = y_property.start return x_property, y_property 

Agora estamos prontos para desenhar nossa programação.

 def draw_plot(client, thing, block, page): photo = page.children.add_new(ImageBlock) photo.move_to(block, "after") array = get_lines_array(thing, client) print(array) for i in range(1, len(array)): points = reparse_points(array[i - 1:i][0]) plt.plot(points[0], points[1], color="red") if not path.exists("images"): os.mkdir("images") if thing["x"] == "date": x_axis_dates() filename = "images/" + random_string(15) + ".png" plt.savefig(filename) print("Uploading " + filename) photo.upload_file(filename) 

Aqui nós adicionamos um novo bloco (com foto), mova-o sob a descrição do gráfico. Em seguida, analisamos novamente os pontos (veja abaixo), desenhamos linhas usando matplotlib, salvamos a imagem resultante com um nome de arquivo aleatório e carregamos no bloco de imagens.

Podemos obter um nome de arquivo aleatório como este:

 def random_string(string_length=10): letters = string.ascii_lowercase return ''.join(random.choice(letters) for i in range(string_length)) 

E precisamos analisar novamente os pontos devido ao fato de o matplotlib aceitar uma representação de dados diferente da maneira como ela é implementada atualmente.

 def reparse_points(points): return [ [points[0][0], points[1][0]], [points[0][1], points[1][1]], ] 

Se você observar atentamente, o método ainda verificará se os dados que temos ao longo do eixo X são a data. Se estiverem, basta exibi-los corretamente:

 def x_axis_dates(ax=None, fig=None): if ax is None: ax = plt.gca() if fig is None: fig = plt.gcf() loc = mdates.AutoDateLocator() fmt = mdates.AutoDateFormatter(loc) ax.xaxis.set_major_locator(loc) ax.xaxis.set_major_formatter(fmt) fig.autofmt_xdate() 

Agora, escreveremos uma função que iniciará um novo thread quando recebermos uma solicitação POST.
Por que POST? Por precaução, para que, se alguém de repente olhe para você, o script não inicie.

Por que exatamente o novo tópico? O IFTTT, que usaremos como um gatilho para que isso funcione, não gosta quando leva muito tempo para esperar por uma resposta do servidor (e, no nosso caso, pode levar muito tempo) e, depois de algum tempo, pode parar de desencadear a compreensão.

 @csrf_exempt def index(request): if request.method == "POST": thread = Thread(target=plot) thread.start() return HttpResponse("Hello, world.") else: return HttpResponse("Hello, world.") 

Parte 5. IFTTT


Vá para a guia criação de applet. Selecionamos o gatilho (no nosso caso, é Data e hora), definimos “a cada hora”. Selecionamos o Webhook acionado (ou seja, “isso”), especificamos nosso endereço local (até o momento) para testá-lo. Bem, é isso. Teste.

Carregar no Heroku


Você pensou que estávamos mexendo com esse gatilho do IFTTT - isso não é para pagar. Heroku oferece uma taxa gratuita para hospedar nossas coisas. A principal coisa é que o serviço dorme pelo menos 6 horas. E ele definitivamente dormirá, porque o chamamos para trabalhar a cada hora, e não a cada minuto.

Além disso, fazemos o seguinte. Vá para heroku para criar um novo projeto . Em seguida, instale o cliente no sistema operacional. E então fazemos tudo de acordo com as instruções que apareceram após a criação do aplicativo.

Depois de baixar tudo no heroku, acesse nosso applet e edite o URL para um novo.

Parte 5. IFTTT


Obrigado a todos que leram para este lugar. Espero que este artigo tenha ajudado você com alguma coisa.

Você pode ler meus outros dois artigos sobre o Noção:

Exporte automaticamente o Google Forms para o Notion usando IFTTT e Django

Criando uma biblioteca doméstica com o Notion e o Python

Source: https://habr.com/ru/post/pt485542/


All Articles