Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,10 +1,7 @@
|
|
| 1 |
-
import os
|
| 2 |
-
# Говорим системе игнорировать прокси для локального адреса
|
| 3 |
-
os.environ['NO_PROXY'] = '127.0.0.1,localhost'
|
| 4 |
-
|
| 5 |
import gradio as gr
|
| 6 |
import pandas as pd
|
| 7 |
import pdfplumber
|
|
|
|
| 8 |
import tempfile
|
| 9 |
import re
|
| 10 |
from datetime import datetime
|
|
@@ -14,7 +11,7 @@ from reportlab.pdfbase import pdfmetrics
|
|
| 14 |
from reportlab.pdfbase.ttfonts import TTFont
|
| 15 |
from reportlab.lib import colors
|
| 16 |
|
| 17 |
-
# --- ЛОГИКА
|
| 18 |
|
| 19 |
class KDChecker:
|
| 20 |
def __init__(self):
|
|
@@ -34,7 +31,6 @@ class KDChecker:
|
|
| 34 |
xls = pd.read_excel(excel_path, sheet_name=None, header=None)
|
| 35 |
|
| 36 |
for sheet_name, df_raw in xls.items():
|
| 37 |
-
# print(f"Обработка листа: {sheet_name}") # Скрыл спам в консоль
|
| 38 |
header_row_index = -1
|
| 39 |
cab_col_idx = -1
|
| 40 |
rem_col_idx = -1
|
|
@@ -168,7 +164,6 @@ class KDChecker:
|
|
| 168 |
else:
|
| 169 |
all_pdf_text = ""
|
| 170 |
for file_path in progress.tqdm(files, desc="Чтение PDF"):
|
| 171 |
-
# print(f"Читаю: {os.path.basename(file_path)}")
|
| 172 |
all_pdf_text += self.extract_text(file_path) + "\n"
|
| 173 |
|
| 174 |
pdf_numbers = self.find_all_decimal_numbers(all_pdf_text)
|
|
@@ -238,21 +233,24 @@ class KDChecker:
|
|
| 238 |
form = c.acroForm
|
| 239 |
width, height = A4
|
| 240 |
|
| 241 |
-
# ---
|
| 242 |
font_name = 'Helvetica'
|
| 243 |
-
|
| 244 |
-
local_font = "arial.ttf"
|
| 245 |
|
| 246 |
try:
|
| 247 |
-
#
|
| 248 |
if os.path.exists(local_font):
|
| 249 |
pdfmetrics.registerFont(TTFont('Arial', local_font))
|
| 250 |
font_name = 'Arial'
|
| 251 |
-
|
| 252 |
-
|
|
|
|
| 253 |
font_name = 'Arial'
|
| 254 |
-
|
| 255 |
-
|
|
|
|
|
|
|
| 256 |
|
| 257 |
y = height - 50
|
| 258 |
c.setFont(font_name, 16)
|
|
@@ -285,7 +283,6 @@ class KDChecker:
|
|
| 285 |
paragraphs = task.split('\n')
|
| 286 |
if y < 80: c.showPage(); y = height - 50; c.setFont(font_name, 10)
|
| 287 |
|
| 288 |
-
# Рисуем интерактивный чекбокс
|
| 289 |
form.checkbox(name=f"cb_{cb_id}", x=50, y=y - 10, size=10, buttonStyle='check', forceBorder=True, fillColor=colors.white)
|
| 290 |
cb_id += 1
|
| 291 |
|
|
@@ -321,61 +318,38 @@ class KDChecker:
|
|
| 321 |
|
| 322 |
# --- НОВЫЙ ИНТЕРФЕЙС С CSS ---
|
| 323 |
|
| 324 |
-
# CSS стили для компактности
|
| 325 |
css = """
|
| 326 |
-
/* Убираем лишние отступы у контейнера */
|
| 327 |
.gradio-container { max-width: 95% !important; }
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
.compact_file {
|
| 331 |
-
height: 150px !important;
|
| 332 |
-
min-height: 150px !important;
|
| 333 |
-
max-height: 150px !important;
|
| 334 |
-
overflow: hidden !important;
|
| 335 |
-
}
|
| 336 |
-
|
| 337 |
-
/* Оранжевая кнопка */
|
| 338 |
-
.orange_btn {
|
| 339 |
-
background: #FF7F27 !important;
|
| 340 |
-
border: none !important;
|
| 341 |
-
color: white !important;
|
| 342 |
-
font-weight: bold;
|
| 343 |
-
}
|
| 344 |
.orange_btn:hover { background: #E06010 !important; }
|
| 345 |
-
|
| 346 |
-
/* Убираем футер */
|
| 347 |
footer { display: none !important; }
|
| 348 |
"""
|
| 349 |
|
| 350 |
def create_app():
|
| 351 |
checker = KDChecker()
|
| 352 |
|
| 353 |
-
|
|
|
|
|
|
|
|
|
|
| 354 |
gr.Markdown("## ✅ Генератор чек-листов КД")
|
| 355 |
|
| 356 |
with gr.Row():
|
| 357 |
-
# === КОЛОНКА 1: БАЗА ===
|
| 358 |
with gr.Column(scale=1):
|
| 359 |
gr.Markdown("### 1. База знаний")
|
| 360 |
-
# elem_classes применяет наш CSS класс
|
| 361 |
db_in = gr.File(label="Excel (.xlsx)", type="filepath", elem_classes="compact_file")
|
| 362 |
-
|
| 363 |
with gr.Group():
|
| 364 |
manual_cab = gr.Dropdown(label="Или шкаф вручную", choices=[], interactive=True)
|
| 365 |
db_out = gr.Textbox(label="Статус базы", lines=2, max_lines=3, interactive=False)
|
| 366 |
-
|
| 367 |
-
# Привязка событий
|
| 368 |
db_in.upload(checker.load_excel_db, inputs=[db_in], outputs=[db_out, manual_cab])
|
| 369 |
|
| 370 |
-
# === КОЛОНКА 2: ДОКУМЕНТЫ ===
|
| 371 |
with gr.Column(scale=1):
|
| 372 |
gr.Markdown("### 2. Документация")
|
| 373 |
files_in = gr.File(label="Чертежи (PDF)", file_count="multiple", type="filepath", elem_classes="compact_file")
|
| 374 |
-
|
| 375 |
-
gr.Markdown("") # Пустой разделитель
|
| 376 |
btn = gr.Button("Сформировать чек-лист", variant="primary", elem_classes="orange_btn")
|
| 377 |
|
| 378 |
-
# === РЕЗУЛЬТАТ ===
|
| 379 |
gr.Markdown("### 3. Результат")
|
| 380 |
with gr.Row():
|
| 381 |
with gr.Column(scale=1):
|
|
@@ -383,11 +357,18 @@ def create_app():
|
|
| 383 |
with gr.Column(scale=1):
|
| 384 |
res_pdf = gr.File(label="Скачать готовый PDF")
|
| 385 |
|
| 386 |
-
# Запуск обработки
|
| 387 |
btn.click(checker.check_files, inputs=[files_in, manual_cab], outputs=[res_txt, res_pdf])
|
| 388 |
|
| 389 |
return app
|
| 390 |
|
|
|
|
| 391 |
if __name__ == "__main__":
|
| 392 |
app = create_app()
|
| 393 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import pandas as pd
|
| 3 |
import pdfplumber
|
| 4 |
+
import os
|
| 5 |
import tempfile
|
| 6 |
import re
|
| 7 |
from datetime import datetime
|
|
|
|
| 11 |
from reportlab.pdfbase.ttfonts import TTFont
|
| 12 |
from reportlab.lib import colors
|
| 13 |
|
| 14 |
+
# --- ЛОГИКА ---
|
| 15 |
|
| 16 |
class KDChecker:
|
| 17 |
def __init__(self):
|
|
|
|
| 31 |
xls = pd.read_excel(excel_path, sheet_name=None, header=None)
|
| 32 |
|
| 33 |
for sheet_name, df_raw in xls.items():
|
|
|
|
| 34 |
header_row_index = -1
|
| 35 |
cab_col_idx = -1
|
| 36 |
rem_col_idx = -1
|
|
|
|
| 164 |
else:
|
| 165 |
all_pdf_text = ""
|
| 166 |
for file_path in progress.tqdm(files, desc="Чтение PDF"):
|
|
|
|
| 167 |
all_pdf_text += self.extract_text(file_path) + "\n"
|
| 168 |
|
| 169 |
pdf_numbers = self.find_all_decimal_numbers(all_pdf_text)
|
|
|
|
| 233 |
form = c.acroForm
|
| 234 |
width, height = A4
|
| 235 |
|
| 236 |
+
# --- ШРИФТЫ (ДЛЯ HUGGING FACE) ---
|
| 237 |
font_name = 'Helvetica'
|
| 238 |
+
# На HF файл должен лежать в корне рядом с app.py
|
| 239 |
+
local_font = "arial.ttf"
|
| 240 |
|
| 241 |
try:
|
| 242 |
+
# Сначала ищем локальный файл (который вы должны загрузить на HF)
|
| 243 |
if os.path.exists(local_font):
|
| 244 |
pdfmetrics.registerFont(TTFont('Arial', local_font))
|
| 245 |
font_name = 'Arial'
|
| 246 |
+
# Затем пробуем системный (для Windows тестов)
|
| 247 |
+
elif os.path.exists("C:\\Windows\\Fonts\\arial.ttf"):
|
| 248 |
+
pdfmetrics.registerFont(TTFont('Arial', "C:\\Windows\\Fonts\\arial.ttf"))
|
| 249 |
font_name = 'Arial'
|
| 250 |
+
else:
|
| 251 |
+
print("⚠️ Шрифт arial.ttf не найден! Русский текст может не отображаться.")
|
| 252 |
+
except Exception as e:
|
| 253 |
+
print(f"Ошибка шрифта: {e}")
|
| 254 |
|
| 255 |
y = height - 50
|
| 256 |
c.setFont(font_name, 16)
|
|
|
|
| 283 |
paragraphs = task.split('\n')
|
| 284 |
if y < 80: c.showPage(); y = height - 50; c.setFont(font_name, 10)
|
| 285 |
|
|
|
|
| 286 |
form.checkbox(name=f"cb_{cb_id}", x=50, y=y - 10, size=10, buttonStyle='check', forceBorder=True, fillColor=colors.white)
|
| 287 |
cb_id += 1
|
| 288 |
|
|
|
|
| 318 |
|
| 319 |
# --- НОВЫЙ ИНТЕРФЕЙС С CSS ---
|
| 320 |
|
|
|
|
| 321 |
css = """
|
|
|
|
| 322 |
.gradio-container { max-width: 95% !important; }
|
| 323 |
+
.compact_file { height: 150px !important; min-height: 150px !important; max-height: 150px !important; overflow: hidden !important; }
|
| 324 |
+
.orange_btn { background: #FF7F27 !important; border: none !important; color: white !important; font-weight: bold; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 325 |
.orange_btn:hover { background: #E06010 !important; }
|
|
|
|
|
|
|
| 326 |
footer { display: none !important; }
|
| 327 |
"""
|
| 328 |
|
| 329 |
def create_app():
|
| 330 |
checker = KDChecker()
|
| 331 |
|
| 332 |
+
# Чтобы исправить warning, убираем css и theme отсюда, если версия gradio очень новая,
|
| 333 |
+
# НО для Hugging Face обычно лучше оставить здесь для предпросмотра.
|
| 334 |
+
# Если все еще ругается - перенесем в launch.
|
| 335 |
+
with gr.Blocks(title="Генератор чек-листов КД") as app:
|
| 336 |
gr.Markdown("## ✅ Генератор чек-листов КД")
|
| 337 |
|
| 338 |
with gr.Row():
|
|
|
|
| 339 |
with gr.Column(scale=1):
|
| 340 |
gr.Markdown("### 1. База знаний")
|
|
|
|
| 341 |
db_in = gr.File(label="Excel (.xlsx)", type="filepath", elem_classes="compact_file")
|
|
|
|
| 342 |
with gr.Group():
|
| 343 |
manual_cab = gr.Dropdown(label="Или шкаф вручную", choices=[], interactive=True)
|
| 344 |
db_out = gr.Textbox(label="Статус базы", lines=2, max_lines=3, interactive=False)
|
|
|
|
|
|
|
| 345 |
db_in.upload(checker.load_excel_db, inputs=[db_in], outputs=[db_out, manual_cab])
|
| 346 |
|
|
|
|
| 347 |
with gr.Column(scale=1):
|
| 348 |
gr.Markdown("### 2. Документация")
|
| 349 |
files_in = gr.File(label="Чертежи (PDF)", file_count="multiple", type="filepath", elem_classes="compact_file")
|
| 350 |
+
gr.Markdown("")
|
|
|
|
| 351 |
btn = gr.Button("Сформировать чек-лист", variant="primary", elem_classes="orange_btn")
|
| 352 |
|
|
|
|
| 353 |
gr.Markdown("### 3. Результат")
|
| 354 |
with gr.Row():
|
| 355 |
with gr.Column(scale=1):
|
|
|
|
| 357 |
with gr.Column(scale=1):
|
| 358 |
res_pdf = gr.File(label="Скачать готовый PDF")
|
| 359 |
|
|
|
|
| 360 |
btn.click(checker.check_files, inputs=[files_in, manual_cab], outputs=[res_txt, res_pdf])
|
| 361 |
|
| 362 |
return app
|
| 363 |
|
| 364 |
+
# --- ЗАПУСК ДЛЯ HUGGING FACE ---
|
| 365 |
if __name__ == "__main__":
|
| 366 |
app = create_app()
|
| 367 |
+
# ДЛЯ HUGGING FACE SPACES НУЖНО ПРОСТО .launch() БЕЗ АРГУМЕНТОВ!
|
| 368 |
+
# Мы передаем CSS и Theme сюда, чтобы убрать Warning
|
| 369 |
+
app.launch(
|
| 370 |
+
server_name="0.0.0.0", # Важно для Docker контейнеров
|
| 371 |
+
server_port=7860, # Стандартный порт HF
|
| 372 |
+
css=css, # Передали стили сюда
|
| 373 |
+
theme=gr.themes.Soft() # Передали тему сюда
|
| 374 |
+
)
|