Spaces:
Running
Running
File size: 6,919 Bytes
11a33db 928a3cd ad5f036 71b5660 7b6676b 928a3cd 7b6676b 928a3cd 11a33db 928a3cd 11a33db 928a3cd 7e47a32 928a3cd 11a33db 928a3cd 7e47a32 928a3cd 7e47a32 928a3cd 71b5660 11a33db 928a3cd fc561bf 928a3cd 7b6676b 928a3cd 7b6676b 928a3cd 7b6676b 928a3cd 7b6676b 928a3cd 71b5660 928a3cd fc561bf 928a3cd fc561bf 928a3cd 11a33db 928a3cd 7b6676b 11a33db 928a3cd 11a33db 928a3cd 11a33db 928a3cd 11a33db 928a3cd 11a33db 928a3cd 11a33db 7b6676b 928a3cd 7b6676b 928a3cd 7b6676b 928a3cd 11a33db 928a3cd 7b6676b 11a33db 928a3cd 11a33db 928a3cd 11a33db 928a3cd 11a33db 928a3cd 7b6676b 928a3cd 7b6676b 71b5660 928a3cd 7e47a32 928a3cd 7b6676b 11a33db 928a3cd 7b6676b 928a3cd 7b6676b 928a3cd 7b6676b 928a3cd 11a33db 928a3cd 7b6676b 928a3cd ad5f036 7e47a32 fc561bf |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
import os
from datetime import datetime
import gradio as gr
from transformers import pipeline
import pdfplumber
from docx import Document
from fpdf import FPDF
# ===== НАСТРОЙКИ =====
# Имя шрифта TTF, который лежит в корне Space (Files → root)
FONT_PATH = "DejaVuSans.ttf"
FONT_FAMILY = "DejaVu"
# Максимальная длина текста в одном заходе в модель (по символам)
# Это грубая оценка, чтобы не превышать лимит ~1024 токена у BART
CHUNK_SIZE = 2000
# Ленивая инициализация summarizer, чтобы не грузить модель при импортe
_summarizer = None
def get_summarizer():
global _summarizer
if _summarizer is None:
_summarizer = pipeline(
"summarization",
model="facebook/bart-large-cnn"
)
return _summarizer
# ===== ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ =====
def read_text_from_file(file_path: str) -> str:
"""Читает текст из PDF или TXT."""
if not file_path:
return ""
path_lower = file_path.lower()
# PDF
if path_lower.endswith(".pdf"):
text = []
with pdfplumber.open(file_path) as pdf:
for page in pdf.pages:
page_text = page.extract_text() or ""
text.append(page_text)
return "\n".join(text)
# TXT (или любой другой текстовый)
with open(file_path, "rb") as f:
raw = f.read()
return raw.decode("utf-8", errors="ignore")
def split_into_chunks(text: str, chunk_size: int = CHUNK_SIZE):
"""Режет длинный текст на куски по chunk_size символов."""
text = text.strip()
if len(text) <= chunk_size:
return [text]
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
# стараемся резать по границе предложения/абзаца
if end < len(text):
dot_pos = text.rfind(".", start, end)
newline_pos = text.rfind("\n", start, end)
sep_pos = max(dot_pos, newline_pos)
if sep_pos > start + chunk_size * 0.3:
end = sep_pos + 1
chunks.append(text[start:end].strip())
start = end
return [c for c in chunks if c]
def summarize_long_text(text: str) -> str:
"""Суммаризирует длинный текст по частям и склеивает результат."""
summarizer = get_summarizer()
chunks = split_into_chunks(text)
summaries = []
for chunk in chunks:
# подстрахуемся от совсем коротких кусков
if len(chunk) < 50:
continue
result = summarizer(
chunk,
max_length=200,
min_length=50,
do_sample=False
)
summaries.append(result[0]["summary_text"].strip())
if not summaries:
return "⚠️ Не удалось создать осмысленное резюме (слишком мало текста)."
return "\n\n".join(summaries)
def save_docx(summary_text: str) -> str:
"""Сохраняет резюме в DOCX и возвращает путь к файлу."""
filename = f"summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.docx"
doc = Document()
doc.add_heading("Резюме документа", level=1)
for paragraph in summary_text.split("\n\n"):
doc.add_paragraph(paragraph)
doc.save(filename)
return filename
def save_pdf(summary_text: str) -> str | None:
"""
Сохраняет резюме в PDF и возвращает путь к файлу.
Если шрифт не найден или не подключился — возвращает None,
чтобы не падать с Unicode ошибкой.
"""
if not os.path.exists(FONT_PATH):
# Шрифт не найден — лучше вернуть None, чем падать
return None
filename = f"summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"
pdf = FPDF()
pdf.add_page()
# Регистрируем Unicode-шрифт
try:
pdf.add_font(FONT_FAMILY, "", FONT_PATH, uni=True)
except Exception as e:
# Если даже тут что-то пошло не так — не ломаем всё приложение
print(f"Ошибка подключения шрифта для PDF: {e}")
return None
pdf.set_font(FONT_FAMILY, size=12)
# Пишем текст резюме
for line in summary_text.split("\n"):
pdf.multi_cell(0, 8, line)
pdf.ln(0.5)
pdf.output(filename)
return filename
# ===== ОСНОВНАЯ ФУНКЦИЯ ДЛЯ GRADIO =====
def summarize_file(file) -> tuple[str, str | None, str | None]:
"""
Основной обработчик:
1) читает файл,
2) делает суммаризацию,
3) сохраняет DOCX и PDF.
Возвращает: (текстовое резюме, путь к DOCX, путь к PDF).
"""
if file is None:
return "⚠️ Пожалуйста, загрузите файл.", None, None
try:
text = read_text_from_file(file.name)
if len(text.strip()) < 50:
return "⚠️ Слишком короткий текст для суммаризации.", None, None
summary_text = summarize_long_text(text)
docx_path = save_docx(summary_text)
pdf_path = save_pdf(summary_text)
# Если PDF не создался (нет шрифта) — просто не отдаём файл
return summary_text, docx_path, pdf_path
except Exception as e:
# Логируем в консоль Space, а пользователю — аккуратное сообщение
print(f"Ошибка при суммаризации: {e}")
return f"❌ Ошибка суммаризации: {e}", None, None
# ===== ИНТЕРФЕЙС GRADIO =====
demo = gr.Interface(
fn=summarize_file,
inputs=gr.File(label="Загрузите файл (.pdf или .txt)"),
outputs=[
gr.Textbox(label="Результат суммаризации"),
gr.File(label="Скачать DOCX"),
gr.File(label="Скачать PDF"),
],
title="Eroha Summarizer 🧠",
description=(
"Загрузите документ (PDF или TXT), модель создаст краткое резюме. "
"Результат можно скачать в DOCX и PDF. Для корректного PDF нужен файл шрифта "
f"{FONT_PATH} в корне Space."
),
)
if __name__ == "__main__":
demo.launch()
|