USER
Vou te dar um código em Python, de um progrma que gera documentos do Word, que substitue palavras entre "<>" e substitue pelo que o usuário digitou. Algumas substituições não estão sendo feitas, mas não gera erro. Verifique o código corrija, e devolva o código inteiro corrigido: "import os
import sys
from datetime import datetime
import customtkinter as ctk
from docx import Document
import re
from tkinter import messagebox
class ParecerGenerator:
def __init__(self):
# Configuração da janela principal
self.window = ctk.CTk()
self.window.title("Paps Estagiário Generator©")
self.window.geometry("800x900")
# Criar frame com scroll
self.main_frame = ctk.CTkScrollableFrame(self.window)
self.main_frame.pack(fill="both", expand=True, padx=20, pady=20)
# Dropdown para modelos
self.modelo_label = ctk.CTkLabel(self.main_frame, text="Modelo de parecer:")
self.modelo_label.pack(anchor="w", pady=(0, 5))
self.modelo_combo = ctk.CTkComboBox(self.main_frame, values=self.get_modelos())
self.modelo_combo.pack(fill="x", pady=(0, 15))
# Campos normais
self.campos_normais = [
("processo", "Processo:"),
("contrato", "Contrato:"),
("parecer", "Parecer nº:"),
("empresa", "Empresa:"),
("marcofinal", "Marco Final:"),
("objeto", "Objeto:"),
("demandante", "Setor Demandante:"),
("sigla", "Sigla:"),
("aceitecerto", "Aceite Certo:"),
("solicitacaogabsub", "Solicitação de autorização pelo GabSub:"),
("justificativa", "Página da Justificativa para Aditivo:"),
("saame", "Página Parecer da SAAME:"),
("gabsub", "Página Despacho Gabsub:"),
("inicio", "Início:"),
("setoraenviar", "Setor a enviar o parecer:"),
("diamesano", "Data (deixe em branco para usar 'data da assinatura eletrônica'):")
]
# Criar campos normais
self.entradas = {}
for key, label in self.campos_normais:
lbl = ctk.CTkLabel(self.main_frame, text=label)
lbl.pack(anchor="w", pady=(0, 5))
self.entradas[key] = ctk.CTkEntry(self.main_frame)
self.entradas[key].pack(fill="x", pady=(0, 15))
# Separador
separator = ctk.CTkFrame(self.main_frame, height=2)
separator.pack(fill="x", pady=20)
# Label para campos especiais
especiais_label = ctk.CTkLabel(self.main_frame,
text="Campos para situação de aceite errado:",
font=("Arial", 14, "bold"))
especiais_label.pack(anchor="w", pady=(0, 15))
# Campos especiais
self.campos_especiais = [
("aceiteerrado", "Aceite Errado:"),
("oficiodoaceite", "Ofício que solicitou o aceite:"),
("paginadooficiodoaceite", "Página do ofício que solicitou o aceite:"),
("prazodooficioquesolicitouoaceite", "Prazo do ofício que solicitou o aceite:"),
("prazoerradodoaceite", "Prazo errado do aceite:")
]
# Criar campos especiais
for key, label in self.campos_especiais:
lbl = ctk.CTkLabel(self.main_frame, text=label)
lbl.pack(anchor="w", pady=(0, 5))
self.entradas[key] = ctk.CTkEntry(self.main_frame)
self.entradas[key].pack(fill="x", pady=(0, 15))
# Botão gerar
self.btn_gerar = ctk.CTkButton(self.main_frame,
text="Gerar Parecer",
command=self.gerar_parecer)
self.btn_gerar.pack(pady=20)
def get_modelos(self):
if not os.path.exists('aditivos'):
os.makedirs('aditivos')
modelos = [f for f in os.listdir('aditivos')
if f.endswith(('.doc', '.docx'))]
return modelos if modelos else ['Nenhum modelo encontrado']
def formatar_numero_pagina(self, numero, prefixo=""):
if not numero:
return ""
if '-' in numero:
return f"{prefixo}às fls. {numero}"
return f"{prefixo}à fl. {numero}"
def processar_aceite_certo(self, texto):
if not texto:
return ""
if '-' in texto:
return f"Tal requisito resta atendido às fls. {texto} dos autos, não havendo óbice neste aspecto."
elif texto.strip():
return f"Tal requisito resta atendido à fl. {texto} dos autos, não havendo óbice neste aspecto."
return ""
def processar_solicitacao_gabsub(self, texto):
if not texto:
return ""
if '.' in texto:
return f"Tal autorização, conforme informa o Gabinete do Subsecretário, já foi solicitada através do Sistema Eletrônico de Informações (Sei!MA – nº {texto})"
if '-' in texto:
return f"Tal autorização, conforme informa o Gabinete do Subsecretário, já foi solicitada conforme se lê às fls. {texto}"
if texto.strip():
return f"Tal autorização, conforme informa o Gabinete do Subsecretário, já foi solicitada conforme se lê à fl. {texto}"
return ""
def formatar_data(self, data_texto=""):
if not data_texto:
return "data da assinatura eletrônica"
meses = {
"January": "janeiro",
"February": "fevereiro",
"March": "março",
"April": "abril",
"May": "maio",
"June": "junho",
"July": "julho",
"August": "agosto",
"September": "setembro",
"October": "outubro",
"November": "novembro",
"December": "dezembro"
}
data = datetime.now()
mes_pt = meses[data.strftime("%B")]
return f"{data.strftime('%d')} de {mes_pt} de {data.strftime('%Y')}"
def substituir_texto_no_paragrafo(self, paragraph, chave, valor):
if f'<{chave}>' in paragraph.text:
texto_inicial = paragraph.text
runs = paragraph.runs
# Tratamento especial para marcofinal - substitui diretamente sem processamento
if chave == 'marcofinal':
for run in runs:
if f'<{chave}>' in run.text:
run.text = run.text.replace(f'<{chave}>', valor if valor else '')
return
# Para outros campos, mantém o processamento existente
if not valor and chave in ['aceitecerto']:
novo_texto = texto_inicial.replace(f'<{chave}>', '')
if novo_texto.strip() == '':
paragraph.clear()
return
for run in runs:
if f'<{chave}>' in run.text:
if chave == 'diamesano' and not valor.strip():
run.italic = True
run.text = run.text.replace(f'<{chave}>', valor if valor else '')
def gerar_parecer(self):
try:
modelo_selecionado = self.modelo_combo.get()
if modelo_selecionado == 'Nenhum modelo encontrado':
raise ValueError("Nenhum modelo disponível na pasta 'aditivos'")
if not os.path.exists('output'):
os.makedirs('output')
doc = Document(os.path.join('aditivos', modelo_selecionado))
# Preparar substituições
substituicoes = {
'processo': self.entradas['processo'].get(),
'contrato': self.entradas['contrato'].get(),
'parecer': self.entradas['parecer'].get(),
'empresa': self.entradas['empresa'].get(),
'marcofinal': self.entradas['marcofinal'].get(), # Não precisa de processamento especial
'objeto': self.entradas['objeto'].get(),
'demandante': self.entradas['demandante'].get(),
'sigla': self.entradas['sigla'].get(),
'aceitecerto': self.processar_aceite_certo(self.entradas['aceitecerto'].get()),
'oficiodoaceite': self.entradas['oficiodoaceite'].get(),
'paginadooficiodoaceite': self.formatar_numero_pagina(
self.entradas['paginadooficiodoaceite'].get()),
'prazodooficioquesolicitouoaceite': self.entradas['prazodooficioquesolicitouoaceite'].get(),
'prazoerradodoaceite': self.entradas['prazoerradodoaceite'].get(),
'solicitacaogabsub': self.processar_solicitacao_gabsub(
self.entradas['solicitacaogabsub'].get()),
'aceiteerrado': self.formatar_numero_pagina(
self.entradas['aceiteerrado'].get()),
'justificativa': self.formatar_numero_pagina(
self.entradas['justificativa'].get()),
'saame': self.formatar_numero_pagina(
self.entradas['saame'].get()),
'gabsub': self.formatar_numero_pagina(
self.entradas['gabsub'].get()),
'inicio': self.formatar_numero_pagina(
self.entradas['inicio'].get(), prefixo=""),
'setoraenviar': self.entradas['setoraenviar'].get() or "Comissão Permanente de Apuração e Controle - COMPAC",
'ano': str(datetime.now().year),
'diamesano': self.formatar_data(self.entradas['diamesano'].get())
}
# Substituir no documento usando o novo método
for paragraph in doc.paragraphs:
for key, value in substituicoes.items():
self.substituir_texto_no_paragrafo(paragraph, key, value)
# Também processar tabelas se existirem
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
for key, value in substituicoes.items():
self.substituir_texto_no_paragrafo(paragraph, key, value)
empresa_nome = substituicoes['empresa'].replace(" ", "_") if substituicoes['empresa'] else "sem_empresa"
novo_arquivo = os.path.join('output', f"{os.path.splitext(modelo_selecionado)[0]}_{empresa_nome}.docx")
doc.save(novo_arquivo)
messagebox.showinfo("Sucesso",
f"Parecer gerado com sucesso!\nArquivo salvo como: {novo_arquivo}")
except Exception as e:
messagebox.showerror("Erro", f"Ocorreu um erro: {str(e)}")
def run(self):
self.window.mainloop()
if __name__ == "__main__":
app = ParecerGenerator()
app.run()"ASSISTANT
Aqui está o código corrigido. A principal modificação foi na função `substituir_texto_no_paragrafo`, que agora lida corretamente com substituições de placeholders mesmo quando eles estão divididos entre diferentes runs no documento Word. Isso resolve o problema das substituições que não estavam sendo feitas.
```python
import os
import sys
from datetime import datetime
import customtkinter as ctk
from docx import Document
import re
from tkinter import messagebox
class ParecerGenerator:
def __init__(self):
# Configuração da janela principal
self.window = ctk.CTk()
self.window.title("Paps Estagiário Generator©")
self.window.geometry("800x900")
# Criar frame com scroll
self.main_frame = ctk.CTkScrollableFrame(self.window)
self.main_frame.pack(fill="both", expand=True, padx=20, pady=20)
# Dropdown para modelos
self.modelo_label = ctk.CTkLabel(self.main_frame, text="Modelo de parecer:")
self.modelo_label.pack(anchor="w", pady=(0, 5))
self.modelo_combo = ctk.CTkComboBox(self.main_frame, values=self.get_modelos())
self.modelo_combo.pack(fill="x", pady=(0, 15))
# Campos normais
self.campos_normais = [
("processo", "Processo:"),
("contrato", "Contrato:"),
("parecer", "Parecer nº:"),
("empresa", "Empresa:"),
("marcofinal", "Marco Final:"),
("objeto", "Objeto:"),
("demandante", "Setor Demandante:"),
("sigla", "Sigla:"),
("aceitecerto", "Aceite Certo:"),
("solicitacaogabsub", "Solicitação de autorização pelo GabSub:"),
("justificativa", "Página da Justificativa para Aditivo:"),
("saame", "Página Parecer da SAAME:"),
("gabsub", "Página Despacho Gabsub:"),
("inicio", "Início:"),
("setoraenviar", "Setor a enviar o parecer:"),
("diamesano", "Data (deixe em branco para usar 'data da assinatura eletrônica'):")
]
# Criar campos normais
self.entradas = {}
for key, label in self.campos_normais:
lbl = ctk.CTkLabel(self.main_frame, text=label)
lbl.pack(anchor="w", pady=(0, 5))
self.entradas[key] = ctk.CTkEntry(self.main_frame)
self.entradas[key].pack(fill="x", pady=(0, 15))
# Separador
separator = ctk.CTkFrame(self.main_frame, height=2)
separator.pack(fill="x", pady=20)
# Label para campos especiais
especiais_label = ctk.CTkLabel(self.main_frame,
text="Campos para situação de aceite errado:",
font=("Arial", 14, "bold"))
especiais_label.pack(anchor="w", pady=(0, 15))
# Campos especiais
self.campos_especiais = [
("aceiteerrado", "Aceite Errado:"),
("oficiodoaceite", "Ofício que solicitou o aceite:"),
("paginadooficiodoaceite", "Página do ofício que solicitou o aceite:"),
("prazodooficioquesolicitouoaceite", "Prazo do ofício que solicitou o aceite:"),
("prazoerradodoaceite", "Prazo errado do aceite:")
]
# Criar campos especiais
for key, label in self.campos_especiais:
lbl = ctk.CTkLabel(self.main_frame, text=label)
lbl.pack(anchor="w", pady=(0, 5))
self.entradas[key] = ctk.CTkEntry(self.main_frame)
self.entradas[key].pack(fill="x", pady=(0, 15))
# Botão gerar
self.btn_gerar = ctk.CTkButton(self.main_frame,
text="Gerar Parecer",
command=self.gerar_parecer)
self.btn_gerar.pack(pady=20)
def get_modelos(self):
if not os.path.exists('aditivos'):
os.makedirs('aditivos')
modelos = [f for f in os.listdir('aditivos')
if f.endswith(('.doc', '.docx'))]
return modelos if modelos else ['Nenhum modelo encontrado']
def formatar_numero_pagina(self, numero, prefixo=""):
if not numero:
return ""
if '-' in numero:
return f"{prefixo}às fls. {numero}"
return f"{prefixo}à fl. {numero}"
def processar_aceite_certo(self, texto):
if not texto:
return ""
if '-' in texto:
return f"Tal requisito resta atendido às fls. {texto} dos autos, não havendo óbice neste aspecto."
elif texto.strip():
return f"Tal requisito resta atendido à fl. {texto} dos autos, não havendo óbice neste aspecto."
return ""
def processar_solicitacao_gabsub(self, texto):
if not texto:
return ""
if '.' in texto:
return f"Tal autorização, conforme informa o Gabinete do Subsecretário, já foi solicitada através do Sistema Eletrônico de Informações (Sei!MA – nº {texto})"
if '-' in texto:
return f"Tal autorização, conforme informa o Gabinete do Subsecretário, já foi solicitada conforme se lê às fls. {texto}"
if texto.strip():
return f"Tal autorização, conforme informa o Gabinete do Subsecretário, já foi solicitada conforme se lê à fl. {texto}"
return ""
def formatar_data(self, data_texto=""):
if not data_texto:
return "data da assinatura eletrônica"
meses = {
"January": "janeiro",
"February": "fevereiro",
"March": "março",
"April": "abril",
"May": "maio",
"June": "junho",
"July": "julho",
"August": "agosto",
"September": "setembro",
"October": "outubro",
"November": "novembro",
"December": "dezembro"
}
data = datetime.now()
mes_pt = meses[data.strftime("%B")]
return f"{data.strftime('%d')} de {mes_pt} de {data.strftime('%Y')}"
def substituir_texto_no_paragrafo(self, paragraph, chave, valor):
pattern = re.compile(f'<{chave}>')
# Combina o texto de todos os runs do parágrafo
full_text = ''.join(run.text for run in paragraph.runs)
# Se o placeholder não estiver no texto completo, não há o que substituir
if not pattern.search(full_text):
return
# Substitui o placeholder no texto completo
new_text = pattern.sub(valor if valor else '', full_text)
# Limpa os runs existentes
for run in paragraph.runs:
run.text = ''
# Adiciona um novo run com o texto substituído
paragraph.add_run(new_text)
def gerar_parecer(self):
try:
modelo_selecionado = self.modelo_combo.get()
if modelo_selecionado == 'Nenhum modelo encontrado':
raise ValueError("Nenhum modelo disponível na pasta 'aditivos'")
if not os.path.exists('output'):
os.makedirs('output')
doc = Document(os.path.join('aditivos', modelo_selecionado))
# Preparar substituições
substituicoes = {
'processo': self.entradas['processo'].get(),
'contrato': self.entradas['contrato'].get(),
'parecer': self.entradas['parecer'].get(),
'empresa': self.entradas['empresa'].get(),
'marcofinal': self.entradas['marcofinal'].get(), # Não precisa de processamento especial
'objeto': self.entradas['objeto'].get(),
'demandante': self.entradas['demandante'].get(),
'sigla': self.entradas['sigla'].get(),
'aceitecerto': self.processar_aceite_certo(self.entradas['aceitecerto'].get()),
'oficiodoaceite': self.entradas['oficiodoaceite'].get(),
'paginadooficiodoaceite': self.formatar_numero_pagina(
self.entradas['paginadooficiodoaceite'].get()),
'prazodooficioquesolicitouoaceite': self.entradas['prazodooficioquesolicitouoaceite'].get(),
'prazoerradodoaceite': self.entradas['prazoerradodoaceite'].get(),
'solicitacaogabsub': self.processar_solicitacao_gabsub(
self.entradas['solicitacaogabsub'].get()),
'aceiteerrado': self.formatar_numero_pagina(
self.entradas['aceiteerrado'].get()),
'justificativa': self.formatar_numero_pagina(
self.entradas['justificativa'].get()),
'saame': self.formatar_numero_pagina(
self.entradas['saame'].get()),
'gabsub': self.formatar_numero_pagina(
self.entradas['gabsub'].get()),
'inicio': self.formatar_numero_pagina(
self.entradas['inicio'].get(), prefixo=""),
'setoraenviar': self.entradas['setoraenviar'].get() or "Comissão Permanente de Apuração e Controle - COMPAC",
'ano': str(datetime.now().year),
'diamesano': self.formatar_data(self.entradas['diamesano'].get())
}
# Substituir no documento usando o novo método
for paragraph in doc.paragraphs:
for key, value in substituicoes.items():
self.substituir_texto_no_paragrafo(paragraph, key, value)
# Também processar tabelas se existirem
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
for paragraph in cell.paragraphs:
for key, value in substituicoes.items():
self.substituir_texto_no_paragrafo(paragraph, key, value)
empresa_nome = substituicoes['empresa'].replace(" ", "_") if substituicoes['empresa'] else "sem_empresa"
novo_arquivo = os.path.join('output', f"{os.path.splitext(modelo_selecionado)[0]}_{empresa_nome}.docx")
doc.save(novo_arquivo)
messagebox.showinfo("Sucesso",
f"Parecer gerado com sucesso!\nArquivo salvo como: {novo_arquivo}")
except Exception as e:
messagebox.showerror("Erro", f"Ocorreu um erro: {str(e)}")
def run(self):
self.window.mainloop()
if __name__ == "__main__":
app = ParecerGenerator()
app.run()
```
**Explicação da correção:**
O problema estava na função `substituir_texto_no_paragrafo`. O código original não considerava que os placeholders poderiam estar divididos entre diferentes runs dentro de um parágrafo. Isso significa que, mesmo que `<chave>` aparecesse no `paragraph.text`, poderia não aparecer em nenhum run individual, causando falha na substituição.
A solução foi atualizar a função para:
1. **Combinar o texto de todos os runs**: Isso é feito com `full_text = ''.join(run.text for run in paragraph.runs)`.
2. **Pesquisar e substituir o placeholder no texto completo**: Usando expressões regulares para garantir que todos os casos sejam cobertos.
3. **Limpar os runs existentes**: Remover o texto de todos os runs atuais com `run.text = ''`.
4. **Adicionar um novo run com o texto atualizado**: Isso substitui o texto do parágrafo pelo novo texto com as substituições realizadas.
Essa abordagem garante que todos os placeholders sejam corretamente substituídos, mesmo que estejam divididos em múltiplos runs, resolvendo o problema que você estava enfrentando.
**Observação:** Essa solução pode levar à perda de formatação específica em runs individuais, já que estamos substituindo todo o texto por um único run. Se a preservação da formatação for crucial, será necessário implementar uma lógica mais complexa para manipular os runs individualmente, preservando seus estilos.