import os
from time import sleep
from dotenv import load_dotenv
from google import genai
from reportlab.platypus import Paragraph, Frame
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.pagesizes import A4
from reportlab.lib.utils import ImageReader
from reportlab.pdfgen import canvas
import re
from html import escape
load_dotenv()
api_token = os.environ.get("SYNAP")
class Model():
def __init__(self, titulo_pdf, titulo, n_paginas):
self.titulo = titulo
self.n_paginas = n_paginas
self.oppo = genai.Client(api_key=api_token, http_options={"api_version": "v1beta"})
print(f"Escolhendo os Subtemas")
self.subtemas = self.API(f"""
Sobre o tema {titulo}, cria {self.n_paginas} subtemas curtos para um trabalho acadêmico.
- O primeiro subtema deve ser 'Introducao'.
- NÃO inclua o tema principal nos subtemas.
- Cada subtema deve ser curto, claro, bem escrito e **APENAS UMA PALAVRA**, sem espaços ou underscores.
- ENTREGUE apenas os subtemas, separados por vírgula ou espaço.
- Não use hífens, números ou símbolos.
- Nenhuma explicação ou frase adicional, apenas os nomes dos subtemas.
""").split(",") # ou .split() se separar por espaços
sleep(5)
print(f"Subtemas Escolhidos!")
self.largura, self.altura = A4
self.c = canvas.Canvas(titulo_pdf, pagesize=A4)
self.pagina = 0
self.pagina_idc = 0
def capa(self, logo, capaImage, titulo, subtitulo, instituto):
self.logo = ImageReader(logo)
self.capaImage = ImageReader(capaImage)
self.titulo = titulo
self.instituto = instituto
self.professor = professor
x = (self.largura - 350)
y = self.altura - 150
self.c.drawImage(self.logo, x + 15, y, width=100, height=100)
self.c.setFont("Times-Roman", 10)
self.c.drawCentredString(x + 65, 675, "República de Angola")
self.c.setFont("Times-Roman", 10)
self.c.drawCentredString(x + 65, 660, "Ministério da Educação")
self.c.setFont("Times-Roman", 10)
self.c.drawCentredString(x + 65, 645, instituto)
self.c.setFont("Times-Bold", 35)
self.c.drawCentredString(x + 65, 580, titulo)
self.c.setFont("Times-Roman", 20)
self.c.drawCentredString(x + 65, 550, subtitulo)
self.c.drawImage(self.capaImage, (x - 65) / 2, (self.altura - 610), width=(576 + 270)/2, height=(315 + 200)/2)
margem = 20
self.c.rect(margem, margem, self.largura - 2*margem, self.altura - 2*margem)
self.c.setFont("Times-Roman", 15)
self.c.drawCentredString(x + 165, 150, "Docente")
self.c.line(320, 100, 490, 100)
def integrantes(self, integrantes, disciplina, grupo, classe, turma, curso, turno):
self.c.showPage()
self.paginar_letra("I")
y = 700
self.c.setFont("Times-Bold", 20)
self.c.drawCentredString(200, 750, "Integrantes do Grupo")
self.integrantes = integrantes
numero = 1
for i in integrantes:
self.c.setFont("Times-Roman", 15)
self.c.drawString(110, y, f"{numero}.")
self.c.drawString(130, y, f"{i}")
y -= 25
numero += 1
self.c.setFont("Times-Bold", 15)
self.c.drawString(110, 300, "Disciplina: ")
self.c.setFont("Times-Roman", 15)
self.c.drawString(190, 300, disciplina)
self.c.setFont("Times-Bold", 15)
self.c.drawString(110, 280, "Grupo: ")
self.c.setFont("Times-Roman", 15)
self.c.drawString(165, 280, grupo)
self.c.setFont("Times-Bold", 15)
self.c.drawString(110, 260, "Classe: ")
self.c.setFont("Times-Roman", 15)
self.c.drawString(165, 260, classe)
self.c.setFont("Times-Bold", 15)
self.c.drawString(110, 240, "Turma: ")
self.c.setFont("Times-Roman", 15)
self.c.drawString(165, 240, turma)
self.c.setFont("Times-Bold", 15)
self.c.drawString(110, 220, "Curso: ")
self.c.setFont("Times-Roman", 15)
self.c.drawString(165, 220, curso)
self.c.setFont("Times-Bold", 15)
self.c.drawString(110, 200, "Turno: ")
self.c.setFont("Times-Roman", 15)
self.c.drawString(165, 200, turno)
def agradecimentos(self, prof):
prompt = f"""
Você é um assistente especializado em gerar textos acadêmicos claros e objetivos.
Tarefa: Criar um texto de AGRADECIMENTOS para um trabalho acadêmico.
- O texto deve **randomizar destinatários**: pais, professores, IAs/pesquisadores, outros apoiadores.
- Não pergunte nomes, apenas use expressões genéricas como “nossos pais”, “professores”, “pesquisadores” etc.
Regras obrigatórias:
1. O texto deve ter aproximadamente 3 linhas.
2. Entregue APENAS o texto dos agradecimentos, sem títulos, notas ou explicações.
3. Não use símbolos de Markdown (** , ##, ```), apenas HTML permitido:
- ... para negrito
-
para quebra de linha
-
para separar parágrafos
4. Cada parágrafo deve ser curto e respeitar a divisão com
5. Mantenha tom respeitoso, formal e acadêmico.
Entrega:
- Texto pronto para inserir no PDF usando ReportLab, com tags HTML respeitadas.
"""
print(f"Criando os Agradecimentos - Página {self.pagina}")
texto = self.API(prompt)
sleep(5)
self.c.showPage()
self.paginar_letra("II")
self.c.setFont("Times-Bold", 17)
self.c.drawCentredString(595/2, 750, "Agradecimentos")
styles = getSampleStyleSheet()
text = f"{texto}"
meust = ParagraphStyle(
"Marius",
parent=styles["Normal"],
fontName="Times-Roman",
fontSize=12,
alignment=4
)
frame = Frame(100, 100, 400, 630, showBoundary=0)
p = Paragraph(text, meust)
frame.addFromList([p], self.c)
def dedicatoria(self):
prompt = f"""
Você é um assistente especializado em gerar textos acadêmicos claros e objetivos.
Tarefa: Criar uma **DEDICATÓRIA** curta para um trabalho acadêmico.
- Deve ter aproximadamente 3 a 4 linhas.
- Entregue apenas o texto da dedicatória, sem títulos ou explicações.
- Não use símbolos de Markdown (** , ##, ```), apenas HTML permitido:
- ... para negrito
-
para quebra de linha
-
para separar parágrafos
- Use tom respeitoso, formal e acadêmico.
Entrega:
- Texto pronto para inserir no PDF usando ReportLab, com tags HTML respeitadas.
"""
print(f"Criando a Dedicatória - Página {self.pagina}")
texto = self.API(prompt)
sleep(5)
self.c.showPage()
self.paginar_letra("III")
self.c.setFont("Times-Bold", 17)
self.c.drawCentredString(595/2, 750, "Dedicatória")
styles = getSampleStyleSheet()
text = f"{texto}"
meust = ParagraphStyle(
"Marius",
parent=styles["Normal"],
fontName="Times-Roman",
fontSize=12,
alignment=4
)
frame = Frame(100, 100, 400, 630, showBoundary=0)
p = Paragraph(text, meust)
frame.addFromList([p], self.c)
def epigrafe(self):
prompt = "Manda uma frase famosa e aleatoria, com duplos sentidos, OBS: apenas quero a frase, mais nada escrito, interface totalmente limpa, nao usar simbolos como ** ##, apenas texto puro"
print(f"Criando a Epígrafe - Página {self.pagina}")
texto = self.API(prompt)
sleep(5)
self.c.showPage()
self.paginar_letra("IV")
self.c.setFont("Times-Bold", 17)
self.c.drawCentredString(595/2, 750, "Epígrafe")
styles = getSampleStyleSheet()
text = f'"{texto}"'
meust = ParagraphStyle(
"Marius",
parent=styles["Normal"],
fontName="Times-Italic",
fontSize=12,
alignment=2
)
frame = Frame(400, -350, 170, 630, showBoundary=0)
p = Paragraph(text, meust)
frame.addFromList([p], self.c)
def resumo(self):
subtemas_str = ", ".join(self.subtemas) # transforma lista em string legível
prompt = f"""
Faz um resumo acadêmico sobre o tema {self.titulo}.
O resumo deve considerar os seguintes subtemas: {subtemas_str}.
- Entrega apenas o resumo, sem títulos, cabeçalhos, listas ou qualquer outro texto extra.
- Não use símbolos de Markdown como **, ##, ``` ou outros.
- Use texto puro.
- Para separar linhas dentro do parágrafo use
.
- Para separar parágrafos use
(duas quebras de linha).
- O resumo deve ser claro, objetivo e bem estruturado, mencionando de forma geral o que cada subtema vai abordar no trabalho.
"""
print(f"Criando o Resumo - Página {self.pagina}")
texto = self.API(prompt)
sleep(5)
self.c.showPage()
self.paginar_letra("V")
self.c.setFont("Times-Bold", 17)
self.c.drawCentredString(595/2, 750, "Resumo")
styles = getSampleStyleSheet()
text = f"{texto}"
meust = ParagraphStyle(
"Marius",
parent=styles["Normal"],
fontName="Times-Roman",
fontSize=12,
alignment=4
)
frame = Frame(100, 100, 400, 630, showBoundary=0)
p = Paragraph(text, meust)
frame.addFromList([p], self.c)
def indice(self):
print(f"Criando Índice - Página {self.pagina}")
self.c.showPage()
self.paginar_letra("VI")
y = 700
self.c.setFont("Times-Bold", 20)
self.c.drawCentredString(595/2, 750, "Índice")
temas = []
for i in self.subtemas:
temas.append(i.replace("_", " "))
x_inicial = 110
x_limite = 500
temas.append("Conclusão")
temas.append("Referências Bibliográficas")
for nome in temas:
self.pagina_idc +=1
self.c.setFont("Times-Roman", 15)
self.c.drawString(x_inicial, y, f"{nome} ")
largura_nome = self.c.stringWidth(nome, "Times-Roman", 15)
x_atual = x_inicial + largura_nome
largura_ponto = self.c.stringWidth(".", "Times-Roman", 15)
while x_atual + largura_ponto < x_limite:
self.c.drawString(x_atual, y, ".")
x_atual += largura_ponto
if nome == "Conclusão":
self.c.drawString(x_limite + 10, y, "I")
elif nome == "Referências Bibliográficas":
self.c.drawString(x_limite + 10, y, "II")
else:
self.c.drawString(x_limite + 10, y, str(self.pagina_idc))
y -= 25
def desenvolvimento(self):
sub = self.subtemas
sub.append("Conclusão")
temas = []
for t in sub:
temas.append(t.replace("_", " "))
for subtemas in temas:
prompt = f"""
Você é um assistente especializado em produzir DESENVOLVIMENTOS DE TEXTOS ACADÊMICOS claros, objetivos e bem estruturados.
INSTRUÇÕES OBRIGATÓRIAS:
- Tema principal: {self.titulo}
- Subtema a desenvolver: {subtemas}
FORMATO E ESTILO (OBEDIÊNCIA ESTRITA):
1. A saída deve conter APENAS o texto do desenvolvimento. Não inclua títulos, cabeçalhos, explicações, notas, listas, metadados ou comentários.
2. NÃO utilize Markdown, símbolos de Markdown ou blocos de código. NÃO PRODUZA MARKDOWN.
3. É ESTRITAMENTE PROIBIDO usar as seguintes tags ou qualquer variação delas:
, ,
,
, ,
, a , listas ou qualquer tag não listada abaixo.
4. As ÚNICAS tags HTML permitidas são:
-
para quebra curta de linha dentro de um parágrafo.
-
para separar parágrafos (OBRIGATÓRIO: cada novo parágrafo deve iniciar após exatamente
).
- ... apenas para destaque de termos essenciais.
5. Não utilize listas numeradas ou marcadores; escreva apenas em parágrafos contínuos.
6. Mantenha linguagem objetiva, técnica e adequada ao nível acadêmico, sem tom promocional ou opinativo.
7. Cada parágrafo deve conter no máximo 3 a 5 frases bem definidas. Evite parágrafos longos ou misturados.
8. Se o subtema permitir maior aprofundamento, desenvolva de forma completa e detalhada, sem medo de extensão.
- Separe parágrafos com
use mesmo 2, para quebrar a linha duas vezes.
- Dentro de cada parágrafo, use
para quebrar linhas longas quando necessário.
- Não use
,
,
ou Markdown.
- Use apenas para destacar termos importantes.
CONTEÚDO:
- Apresente uma definição clara e direta do subtema no contexto do tema principal.
- Desenvolva de 2 a 4 ideias centrais fundamentais para a compreensão do subtema.
- Finalize com uma frase conclusiva que sintetize a relevância do subtema dentro do tema principal.
EXEMPLO DE FORMATO (APENAS ILUSTRATIVO — NÃO REPRODUZIR):
Parágrafo 1 com explicação inicial...
Parágrafo 2 com conceito importante desenvolvido...
Parágrafo final com síntese do subtema.
ENTREGA:
- Responda SOMENTE com o desenvolvimento final.
- Não inclua linhas em branco extras no início ou no fim.
- Use exclusivamente
para separar parágrafos e apenas quando necessário.
Agora, seguindo rigorosamente TODAS as instruções acima, gere o desenvolvimento solicitado.
"""
print(f"Criando o Subtema {subtemas} - Página {self.pagina + 1}")
texto = self.API(prompt)
print(f"Subtema {subtemas} criado!")
sleep(10)
self.pagina += 1
self.c.showPage()
if subtemas == "Conclusão":
self.paginar_letra("I")
else:
self.paginar(self.pagina)
self.c.setFont("Times-Bold", 17)
self.c.drawCentredString(595/2, 750, subtemas)
styles = getSampleStyleSheet()
text = f"{texto}"
meust = ParagraphStyle(
"Marius",
parent=styles["Normal"],
fontName="Times-Roman",
fontSize=12,
alignment=4
)
frame = Frame(100, 100, 400, 630, showBoundary=0)
p = Paragraph(text, meust)
frame.addFromList([p], self.c)
def ref(self):
prompt = f"""
Você é um assistente que gera REFERÊNCIAS BIBLIOGRÁFICAS em formato padronizado.
TEMA: {self.titulo}
INSTRUÇÕES OBRIGATÓRIAS:
1. Gere EXATAMENTE 5 referências relacionadas ao tema informado.
2. Cada referência deve conter EXATAMENTE 3 linhas:
- 1ª linha: Autor(es) e ano.
- 2ª linha: Título da obra/artigo.
- 3ª linha: Link em formato HTML, usando exclusivamente ....
3. Cada referência deve ser separada da seguinte por exatamente `
`.
4. Não usar listas, não usar markdown, não usar negrito ou itálico nas referências.
5. Não adicionar explicações, textos extras ou comentários — apenas as referências no formato exigido.
6. As referências podem ser fictícias, mas devem parecer verossímeis.
FORMATO EXEMPLO (NÃO reproduzir os dados abaixo):
Autor, Ano
Título da obra
link...
Agora gere as 5 referências conforme o tema.
"""
print(f"Criando o Subtema Referências Bibliográficas - Página {self.pagina + 1}")
texto = self.API(prompt)
print(f" Trabalho finalizado criado!")
#sleep(7)
self.c.showPage()
self.pagina += 1
self.paginar_letra("II")
self.c.setFont("Times-Bold", 17)
self.c.drawCentredString(595/2, 750, "Referências Bibliográficas")
styles = getSampleStyleSheet()
text = f"{texto}"
meust = ParagraphStyle(
"Marius",
parent=styles["Normal"],
fontName="Times-Roman",
fontSize=12,
alignment=4
)
frame = Frame(100, 100, 400, 630, showBoundary=0)
p = Paragraph(text, meust)
frame.addFromList([p], self.c)
def paginar(self, pag):
self.c.drawString(500, 50, str(pag))
def paginar_letra(self, pag):
self.c.drawString(500, 50, pag)
def API(self, prompt):
while True:
try:
sleep(3)
print("Gerando...")
res = self.oppo.models.generate_content(model="gemma-3-27b-it", contents=prompt)
return self.limpar(res.text)
except Exception as e:
print("erro:", e)
print("Tentando novamente")
def limpar(self, texto: str) -> str:
if not texto:
return ""
# Escapa qualquer coisa perigosa primeiro
texto = escape(texto)
# Reverte apenas as tags PERMITIDAS pelo ReportLab
texto = texto.replace("<br/>", "
")
texto = texto.replace("<br>", "
")
texto = texto.replace("<strong>", "")
texto = texto.replace("</strong>", "")
# Normaliza
texto = re.sub(r"
", "
", texto, flags=re.IGNORECASE)
# Remove excesso de quebras
texto = re.sub(r"(?:
){3,}", "
", texto)
return texto.strip()
def save(self):
self.c.save()
def criar_trabalho(logo, capaImage, titulo, subtitulo, instituto, integrantes, prof, disciplina, grupo, classe, turma, curso, turno, n_paginas):
txt = [t[0] for t in integrantes]
txt1 = []
for i in txt:
txt1.append(i)
nn = Model(f"{titulo}.pdf", titulo, n_paginas)
nn.capa(logo, capaImage, titulo, subtitulo, instituto)
nn.integrantes(txt, disciplina, grupo, classe, turma, curso, turno)
nn.agradecimentos(prof)
nn.dedicatoria()
nn.epigrafe()
nn.resumo()
nn.indice()
nn.desenvolvimento()
nn.ref()
nn.save()
return f"{titulo}.pdf"
logo = "logo.png"
titulo = "Alcoolismo em Angola"
inst = "Complexo Escolar Politécnico Girassol"
professor = "Hélder Vunge"
capa = "images.png"
sub = "Alto nível de alcoolismo em Angola"
#criar_trabalho(logo, capa, titulo, sub, inst)
import numpy as np
import gradio as gr
with gr.Blocks(theme="NoCrypt/miku") as demo:
gr.Markdown("# PaperFlow - As tuas mãos invisíveis")
gr.Textbox(
label="ISSO PODE DEMORAR ~5MIN, MINHA CPU É RUINZINHA KKK",
value="DESENVOLVIDO PELA STARTUP CLOSESOURCE (CS)",
interactive=False
)
saida_do_pdf = gr.File(label="BAIXAR PDF")
with gr.Row():
logo = gr.Image(label="LOGO DO INSTITUTO OU ESCOLA", type="filepath")
capaImage = gr.Image(label="IMAGEM DA CAPA", type="filepath")
with gr.Row():
titulo = gr.Textbox(label="TÍTULO OU TEMA")
subtitulo = gr.Textbox(label="SUBTÍTULO")
instituto = gr.Textbox(label="INSTITUTO")
with gr.Row():
disciplina = gr.Textbox(label="DISCIPLINA")
grupo = gr.Textbox(label="GRUPO")
classe = gr.Textbox(label="CLASSE")
turma = gr.Textbox(label="TURMA")
curso = gr.Textbox(label="CURSO")
turno = gr.Textbox(label="TURNO")
with gr.Row():
professor = gr.Textbox(label="NOME DO PROFESSOR")
n_paginas = gr.Textbox(label="NÚMERO DE PÁGINAS (CONTEÚDO)")
integrantes = gr.DataFrame(
headers = ["Nome dos Integrantes"],
row_count=15,
col_count=1,
type="array",
interactive=True,
wrap=True
)
botao_criar = gr.Button("CRIAR TRABALHO")
botao_criar.click(fn=criar_trabalho, inputs=[logo, capaImage, titulo, subtitulo, instituto, integrantes, professor, disciplina, grupo, classe, turma, curso, turno, n_paginas], outputs=[saida_do_pdf])
demo.launch(share=True)