504 lines
16 KiB
Python
Executable File
504 lines
16 KiB
Python
Executable File
import os
|
|
import yaml
|
|
from bs4 import BeautifulSoup
|
|
import json
|
|
import copy
|
|
import shutil
|
|
|
|
# Caminho para o arquivo _quarto.yml
|
|
quarto_config_file = "_quarto.yml"
|
|
|
|
# Caminho para o arquivo _quarto-html.yml
|
|
quarto_html_config_file = "_quarto-html.yml"
|
|
|
|
|
|
|
|
|
|
with open(quarto_config_file) as f:
|
|
|
|
quarto_config = yaml.safe_load(f)
|
|
|
|
|
|
with open(quarto_html_config_file) as f:
|
|
|
|
quarto_html_config = yaml.safe_load(f)
|
|
|
|
|
|
# Extrair as informações do arquivo _quarto.yml
|
|
|
|
|
|
# funcao para iterar os capítulos
|
|
def extrair_arquivos_qmd(objeto, capitulos_arquivos_html):
|
|
|
|
if isinstance(objeto, list):
|
|
|
|
for elemento in objeto:
|
|
|
|
extrair_arquivos_qmd(elemento, capitulos_arquivos_html)
|
|
|
|
elif isinstance(objeto, dict):
|
|
|
|
if 'part' in objeto:
|
|
|
|
extrair_arquivos_qmd(objeto['part'], capitulos_arquivos_html)
|
|
|
|
if 'chapters' in objeto:
|
|
|
|
extrair_arquivos_qmd(objeto['chapters'], capitulos_arquivos_html)
|
|
|
|
elif isinstance(objeto, str) and objeto.endswith('.qmd'):
|
|
|
|
capitulos_arquivos_html.append(objeto[:-4])
|
|
|
|
|
|
|
|
|
|
# pegando o local dos arquivos html
|
|
pasta_livro_renderizado = quarto_html_config["project"]["output-dir"]
|
|
|
|
|
|
# pegando o campo controle-moan para verificar se é um dicionário
|
|
é_dicionário = quarto_config["controle-moan"]["dicionario"]
|
|
|
|
|
|
# pegando os capítulos
|
|
capitulos = quarto_html_config["book"]["chapters"]
|
|
capitulos_arquivo_html = []
|
|
extrair_arquivos_qmd(capitulos, capitulos_arquivo_html)
|
|
|
|
|
|
# Onde ficarao as referencias dos capítulos
|
|
ref_cap = {}
|
|
|
|
|
|
# Letras para 'numerar' os capítulos
|
|
letras = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
|
|
|
|
total_de_letras = len(letras)
|
|
|
|
letras_index_digito_1 = 0
|
|
|
|
# ver qual é a próxima letra para um capítulo nao numerado
|
|
def escolher_letra_para_capitulo(letras_index_digito_1):
|
|
|
|
cap = letras[letras_index_digito_1%total_de_letras]
|
|
|
|
# verifica se o cap precisa de um segundo digito
|
|
if letras_index_digito_1/total_de_letras >= 1:
|
|
|
|
# as duas barras // de divisao pega só a parte inteira da divisao
|
|
letras_index_digito_2 = (letras_index_digito_1 // total_de_letras) - 1
|
|
|
|
cap = letras[letras_index_digito_2%total_de_letras] + letras[letras_index_digito_1%total_de_letras]
|
|
|
|
return cap
|
|
|
|
|
|
|
|
|
|
|
|
# altera os arquivos html
|
|
for index, output_file in enumerate(capitulos_arquivo_html):
|
|
|
|
# Caminho completo para o arquivo HTML
|
|
arquivo_html = os.path.join(pasta_livro_renderizado, output_file + ".html")
|
|
|
|
# conta os elementos que serão uma referencia / Ele começa em 0 na abertura de um novo capítulo
|
|
ref_num = 0
|
|
|
|
# Abre o arquivo HTML
|
|
with open(arquivo_html, "r", encoding="utf-8") as f:
|
|
|
|
content = f.read()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# função colocar as referencias nas tags
|
|
def colocar_referencia(cap, el, ref):
|
|
|
|
# Se a tag tem id, apenas pega o valor e coloca no título da tag e defini unidade_bool false. Se não, cria o id, tb coloca no título da tag e coloca unidade_bool true
|
|
if el.get("id"):
|
|
|
|
el['title'] = el['id']
|
|
|
|
unidade_bool = False
|
|
|
|
|
|
else:
|
|
|
|
# Define o id da referência
|
|
el['id'] = f"{cap}P{ref}"
|
|
|
|
# adiciona o title da referência
|
|
el['title'] = f"{cap}P{ref}"
|
|
|
|
unidade_bool = True
|
|
|
|
|
|
|
|
# Se unidade_bool for true, adiciona a classe unidade; caso contrário, adiciona a classe unidade_silenciosa. Isso caso a tag já nao possua essas classes
|
|
if unidade_bool:
|
|
|
|
if not "unidade" in el.get("class", []):
|
|
|
|
el["class"] = el.get("class", []) + ["unidade"]
|
|
|
|
else:
|
|
|
|
if not "unidade_silenciosa" in el.get("class", []) and "unidade" not in el.get("class", []):
|
|
|
|
el["class"] = el.get("class", []) + ["unidade_silenciosa"]
|
|
|
|
|
|
|
|
|
|
# Cria um objeto BeautifulSoup
|
|
soup = BeautifulSoup(content, "html.parser")
|
|
|
|
|
|
|
|
# Encontre a tag 'main' no documento
|
|
main_tag = soup.find("main")
|
|
|
|
|
|
# Crie o elemento script com os metadados
|
|
script_tag = soup.new_tag('script', data="moan-metadados")
|
|
script_tag.string = f'var metadados = {{livroUrl: "{pasta_livro_renderizado}"}}'
|
|
|
|
# Adicione o script ao cabeçalho (head) do HTML
|
|
soup.head.append(script_tag)
|
|
|
|
|
|
|
|
|
|
|
|
# Se existir o ark, colocar os dados nas tags object do primeiro capítulo do livro
|
|
if "ark" in quarto_html_config["moan-dados"] and index == 0:
|
|
|
|
ark = quarto_html_config["moan-dados"]["ark"]
|
|
|
|
# Substitua "." por "_p_" e "/" por "_b_" em 'ark'
|
|
ark = ark.replace(".", "_p_").replace("/", "_b_")
|
|
|
|
# Adicione "ark_dp_" ao início de 'ark'
|
|
ark = "ark_dp_" + ark
|
|
|
|
# Encontre as tags object com id "metadados_livro_yml" e "metadados_livro_json"
|
|
obj_metadados_yml = main_tag.find("object", {"id": "metadados_livro_yml"})
|
|
|
|
obj_metadados_json = main_tag.find("object", {"id": "metadados_livro_json"})
|
|
|
|
if obj_metadados_yml and obj_metadados_json:
|
|
|
|
# Defina o atributo "data" das tags object
|
|
obj_metadados_yml["data"] = "https://ark.livro.online/yaml/" + ark + ".yml"
|
|
|
|
obj_metadados_json["data"] = "https://ark.livro.online/json/" + ark + ".json"
|
|
|
|
|
|
|
|
# Verifique se o arquivo "postos-de-venda.json" existe
|
|
json_file_path = "postos-de-venda.json"
|
|
|
|
if os.path.exists(json_file_path) and index == 0:
|
|
|
|
# O arquivo JSON existe, abra-o e extraia as chaves e valores
|
|
with open(json_file_path, "r", encoding="utf-8") as json_file:
|
|
|
|
postos_de_venda = json.load(json_file)
|
|
|
|
# Crie uma nova lista não ordenada para armazenar os links
|
|
ul = soup.new_tag("ul", id="postos-de-venda")
|
|
|
|
for key, value in postos_de_venda.items():
|
|
|
|
# Crie uma nova tag de link ("a") com a chave como texto e o valor como src
|
|
link = soup.new_tag("a", href=value)
|
|
|
|
link.string = key # Define o texto do link como a chave
|
|
|
|
# Adicione o link como um item de lista à lista não ordenada
|
|
li = soup.new_tag("li")
|
|
|
|
li.append(link)
|
|
|
|
ul.append(li)
|
|
|
|
# Encontre a tag "section" com ID "versão-impressa"
|
|
section_versao_impressa = main_tag.find("section", id="versão-impressa")
|
|
|
|
if section_versao_impressa:
|
|
|
|
# Adicione a lista não ordenada de links à seção "versão-impressa"
|
|
section_versao_impressa.append(ul)
|
|
|
|
|
|
# Copie o arquivo JSON para a pasta especificada em "pasta_livro_renderizado"
|
|
dest_file = os.path.join(pasta_livro_renderizado, "postos-de-venda.json")
|
|
|
|
shutil.copy(json_file_path, dest_file)
|
|
|
|
|
|
|
|
|
|
|
|
# Definindo o capítulo
|
|
if index == 0:
|
|
|
|
cap = letras[letras_index_digito_1] # É a letra A, tem que ser a letra A
|
|
|
|
é_um_capitulo_numerado = False
|
|
|
|
ref_cap[cap] = capitulos_arquivo_html[index]
|
|
|
|
else:
|
|
|
|
é_um_capitulo_numerado = False
|
|
|
|
é_um_capitulo_verbete = False
|
|
|
|
# Encontre a primeira tag <h1>
|
|
h1_tag = soup.find('h1')
|
|
|
|
# Todos os capítulos devem ter uma tag <h1>
|
|
if h1_tag:
|
|
|
|
# Encontre o primeiro <span> com a classe 'chapter-number' dentro da tag <h1>
|
|
span_tag = h1_tag.find('span', class_='chapter-number')
|
|
|
|
# Verifique se o <span> com a classe 'chapter-number' foi encontrado dentro do <h1>
|
|
if span_tag:
|
|
|
|
cap = span_tag.text
|
|
|
|
é_um_capitulo_numerado = True
|
|
|
|
ref_cap[cap] = capitulos_arquivo_html[index]
|
|
|
|
|
|
|
|
|
|
# Verifique se há pelo menos uma tag <div> com a classe "um_capitulo", ou seja nae é capitulo de verbete caso seja um dicionario
|
|
encontrou_um_capitulo = bool(main_tag.find('div', class_='um_capitulo'))
|
|
|
|
|
|
|
|
# Se é um capítulo normal em um dicionário e nao é um capítulo numerado, seleciona a letra para marcar o capítulo
|
|
# Nao esquecer que deve haver uma div com as classes hidden e um_capitulo em todos os capítulos que nao forem de verbetes em um de um dicionario
|
|
if not encontrou_um_capitulo and é_dicionário:
|
|
|
|
é_um_capitulo_verbete = True
|
|
|
|
# Coloca os marcadores nos verbetes como o nome do próprio verbete
|
|
dt_tags = main_tag.find_all('dt')
|
|
|
|
for dt_tag in dt_tags:
|
|
|
|
# Para contar o numero de definicoes dd
|
|
Num_dd = 0
|
|
|
|
texto_titulo_dt = dt_tag.text
|
|
|
|
texto_a_ser_sanitizado = dt_tag.text
|
|
|
|
dt_tag['class'] = dt_tag.get("class", []) + ["unidade", "verbete"]
|
|
|
|
dt_tag['title'] = texto_titulo_dt
|
|
|
|
id_dt_tag = (texto_a_ser_sanitizado
|
|
|
|
.replace(' ', '_')
|
|
.replace('&', '_e_')
|
|
.replace('$', '_s_')
|
|
.replace('+', '_mais_')
|
|
.replace(',', '_vir_')
|
|
.replace('/', '_barra_')
|
|
.replace(':', '_dois_pontos_')
|
|
.replace(';', '_ponto_vir_')
|
|
.replace('?', '_interrog_')
|
|
.replace('=', '_igual_')
|
|
.replace('@', '_at_')
|
|
.replace('#', '_jv_')
|
|
.replace('>', '_maiq_')
|
|
.replace('<', '_menq_')
|
|
.replace('[', '_abre_colch_')
|
|
.replace(']', '_fecha_col_')
|
|
.replace('{', '_abre_ch_')
|
|
.replace('}', '_fecha_ch_')
|
|
.replace('.', '_ponto_')
|
|
.replace('|', '_barra_ver_')
|
|
.replace('\\', '_barra_inv_')
|
|
.replace('%', '_p_100_')
|
|
.replace('^', '_acen_chapeu_')
|
|
|
|
)
|
|
|
|
dt_tag['id'] = id_dt_tag
|
|
|
|
|
|
|
|
|
|
# Encontre o primeiro elemento irmão <dd> da tag <dt>
|
|
dd_tag = dt_tag.find_next_sibling('dd')
|
|
|
|
# Itere sobre todas as tags <dd> irmãs até encontrar uma tag não <dd>
|
|
while dd_tag and dd_tag.name == 'dd':
|
|
|
|
Num_dd += 1
|
|
|
|
dd_tag['title'] = texto_titulo_dt + " def. " + str(Num_dd)
|
|
|
|
dd_tag['id'] = id_dt_tag + "_def_" + str(Num_dd)
|
|
|
|
# Encontre o próximo elemento irmão <dd> da tag <dd>
|
|
dd_tag = dd_tag.find_next_sibling('dd')
|
|
|
|
|
|
|
|
#verifica de há outras tags além de dt, dd, e títulos (usado para o caso de um capítulo de verbete contiver mais conteúdos)
|
|
tags_procuradas = ['p', 'li', 'tr', 'blockquote', 'code', 'pre']
|
|
|
|
mais_conteudo_alem_verbetes = False
|
|
|
|
if é_um_capitulo_verbete and é_dicionário:
|
|
|
|
for tag in tags_procuradas:
|
|
|
|
# Verifique se a tag está presente em 'main_tag'
|
|
if main_tag.find(tag):
|
|
|
|
# Verifique se a tag não tem 'nav' como pai
|
|
if not main_tag.find(tag).find_parent("nav"):
|
|
|
|
mais_conteudo_alem_verbetes = True
|
|
|
|
break # Se ambas as condições forem atendidas, definimos como True e saímos do loop
|
|
|
|
|
|
|
|
# Verifica se nao é um capítulo sem numero e se nao for um capítulo de verbete ou se é um capitulo de verbete e se existe outro conteudo alem das definicoes dos verbetes
|
|
if (not é_um_capitulo_numerado and not é_um_capitulo_verbete) or (é_dicionário and not é_um_capitulo_numerado and é_um_capitulo_verbete and mais_conteudo_alem_verbetes):
|
|
|
|
letras_index_digito_1 += 1
|
|
|
|
cap = escolher_letra_para_capitulo(letras_index_digito_1)
|
|
|
|
ref_cap[cap] = capitulos_arquivo_html[index]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Iterar sobre todas as tags dentro da tag 'main'
|
|
for tag in main_tag.find_all(True):
|
|
|
|
# Verifique se a classe 'unidade' está presente nos atributos da tag
|
|
if "unidade" in tag.get("class", []):
|
|
|
|
# Incrementa o número da referência
|
|
ref_num += 1
|
|
|
|
# Coloca a referência na tag
|
|
colocar_referencia(cap, tag, ref_num)
|
|
|
|
# Pula para a próxima tag
|
|
continue
|
|
|
|
parent_tag = tag.parent
|
|
|
|
# Não é filho direto de 'td' ou 'li'
|
|
if parent_tag.name not in ["td", "li", "p"]:
|
|
|
|
# Verifique se a tag é filha de uma tag chamada 'header' usando find_parents(). Em caso positivo, pula
|
|
is_descendant_of_header = tag.find_parents("header")
|
|
|
|
if is_descendant_of_header:
|
|
continue
|
|
|
|
|
|
|
|
# Se é um parágrafo
|
|
if tag.name == "p" or tag.name == "tr" or tag.name == "li" or tag.name == "code" or tag.name == "blockquote" or (tag.name == "dt" and not é_um_capitulo_verbete) or (tag.name == "dd" and not é_um_capitulo_verbete) or ("quarto-video" in tag.get("class", []) and tag.name == "div") or ("callout" in tag.get("class", []) and tag.name == "div") or ("csl-entry" in tag.get("class", []) and tag.name == "div"):
|
|
|
|
# Incrementa o número da referência
|
|
ref_num += 1
|
|
|
|
# Coloca a referência na tag
|
|
colocar_referencia(cap, tag, ref_num)
|
|
|
|
# Pula para a próxima tag
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Se for criado pelo usuário um id para euqacoes, aqui coloco a classe unidade_silenciosa para poder ser referenciado quando o usuário solicitar pegar referencia, mas esse tipo de referencia nao deve aparecer na paginacao do capítulo, lá no rodapé
|
|
if "math" in tag.get("class", []) and tag.name == "span":
|
|
|
|
if tag.parent and tag.parent.name == "span":
|
|
|
|
if tag.parent.get("id"):
|
|
|
|
tag.parent["class"] = tag.parent.get("class", []) + ["unidade_silenciosa"]
|
|
|
|
tag.parent["title"] = tag.parent["id"]
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
|
|
# Se tiver as classes de teoremas de matemática, colocar unidade_silenciosa
|
|
if any(classe in tag.get("class", []) for classe in {"theorema", "lemma", "corollary", "proposition", "conjecture", "definition", "example", "exercise"}) and tag.name == "div":
|
|
|
|
if tag.get("id"):
|
|
|
|
tag["class"] = tag.parent.get("class", []) + ["unidade_silenciosa"]
|
|
|
|
tag["title"] = tag.parent["id"]
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Coloca o conteudo dentro de uma div com id chamado de papel, substituindo o conteudo original da tag main pela div papel
|
|
|
|
# Crie uma div com id "papel"
|
|
div_papel = soup.new_tag('div', id='papel')
|
|
|
|
# Copie a tag main
|
|
main_content_copy = copy.copy(main_tag.contents)
|
|
|
|
# Limpe o conteúdo da tag <main>
|
|
main_tag.clear()
|
|
|
|
# Adicione a cópia do conteúdo à div "papel"
|
|
for item_copy in main_content_copy:
|
|
div_papel.append(item_copy)
|
|
|
|
# Acrescentar a div_papel na tag main
|
|
main_tag.append(div_papel)
|
|
|
|
# Salva o arquivo HTML modificado
|
|
with open(arquivo_html, "w", encoding="utf-8") as f:
|
|
|
|
f.write(str(soup))
|
|
|
|
# Gravar os marcadores de referencia do livro em um arquivo JSON. ele esta na variavel ref_cap e essa informacao será útil para o leitor do livro poder entrar na busca digitar a referenci e obter o conteudo.
|
|
nome_arquivo = "ref_capitulos.json"
|
|
|
|
caminho_arquivo = os.path.join(pasta_livro_renderizado, nome_arquivo)
|
|
|
|
with open(caminho_arquivo, "w", encoding="utf-8") as arquivo_json:
|
|
|
|
json.dump(ref_cap, arquivo_json, ensure_ascii=False, indent=4) |