Ed5 commited on
Commit
9a5caff
·
verified ·
1 Parent(s): afab178

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -59
app.py CHANGED
@@ -11,6 +11,10 @@ from reportlab.pdfbase import pdfmetrics
11
  from reportlab.pdfbase.ttfonts import TTFont
12
  from reportlab.lib import colors
13
 
 
 
 
 
14
  class KDChecker:
15
  def __init__(self):
16
  self.excel_db = pd.DataFrame()
@@ -18,7 +22,7 @@ class KDChecker:
18
  self.known_docs = ["Э3", "В4", "ПЭ3", "ВО", "ТЭ5", "СБ", "С5", "ОЛ", "Э1", "Э4", "Э7", "Д3", "Э6"]
19
 
20
  def load_excel_db(self, excel_path):
21
- # Если файл не выбран
22
  if excel_path is None:
23
  return "Файл не выбран", gr.update(choices=[], value=None)
24
 
@@ -26,8 +30,8 @@ class KDChecker:
26
  sheets_log = []
27
 
28
  try:
29
- # Читаем Excel напрямую по пути
30
  xls = pd.read_excel(excel_path, sheet_name=None, header=None)
 
31
 
32
  for sheet_name, df_raw in xls.items():
33
  header_row_index = -1
@@ -65,17 +69,19 @@ class KDChecker:
65
  sheets_log.append(f"Лист '{sheet_name}': заголовки не найдены")
66
 
67
  if not all_data:
 
68
  return "❌ Ошибка: Не найдены заголовки 'Шкаф' и 'Примечание'.", gr.update(choices=[], value=None)
69
 
70
  self.excel_db = pd.concat(all_data, ignore_index=True)
71
  self.cabinet_list = sorted(self.excel_db["Cabinet"].unique().tolist())
72
 
 
73
  msg = f"✅ База знаний загружена успешно!\nВсего записей: {len(self.excel_db)}\nОбработаны листы: {', '.join(sheets_log)}"
74
  return msg, gr.update(choices=self.cabinet_list, value=None, interactive=True)
75
 
76
  except Exception as e:
77
- # Выводим ошибку в интерфейс, чтобы не гадать
78
- return f"❌ Критическая ошибка чтения Excel: {str(e)}", gr.update(choices=[], value=None)
79
 
80
  def extract_text(self, pdf_path):
81
  try:
@@ -84,7 +90,8 @@ class KDChecker:
84
  for page in pdf.pages:
85
  full_text += (page.extract_text() or "") + "\n"
86
  return full_text
87
- except:
 
88
  return ""
89
 
90
  def find_all_decimal_numbers(self, text):
@@ -115,7 +122,6 @@ class KDChecker:
115
 
116
  def get_remarks(self, cabinet_key, is_clean_key=True):
117
  if self.excel_db.empty: return {}
118
-
119
  if is_clean_key:
120
  target = cabinet_key.replace(" ", "")
121
  mask = self.excel_db['Cabinet_Clean'].str.contains(re.escape(target), case=False, na=False)
@@ -124,46 +130,35 @@ class KDChecker:
124
 
125
  rows = self.excel_db[mask]
126
  if rows.empty: return {}
127
-
128
  parsed = {}
129
-
130
  for remark_cell in rows['Remark']:
131
  cell_text = str(remark_cell)
132
  cell_text = re.sub(r'(\d+)\.([А-ЯA-Z])', r'\1. \2', cell_text)
133
-
134
  items = re.split(r'(?:^|\n)\s*(?=\d+[\.\)])', cell_text)
135
-
136
  for item in items:
137
  if len(item) < 3: continue
138
  clean_item = item.strip()
139
  clean_item_no_num = re.sub(r'^\d+[\.\)]\s*', '', clean_item)
140
-
141
  doc_pattern = r'^(?:Документ\s+|В\s+)?([А-ЯA-Z0-9\s,\(\)\-]+?)(?:[\.\:\-]|\s+)(.*)'
142
  match = re.match(doc_pattern, clean_item_no_num, re.IGNORECASE | re.DOTALL)
143
-
144
  detected_docs = []
145
  final_text = clean_item
146
-
147
  if match:
148
  potential_docs_str = match.group(1).upper()
149
  cleaned_codes = potential_docs_str.replace("(", " ").replace(")", " ").replace(",", " ")
150
  parts = cleaned_codes.split()
151
-
152
  valid_parts = [p for p in parts if p in self.known_docs]
153
  if valid_parts:
154
  detected_docs = valid_parts
155
  final_text = match.group(2).strip()
156
-
157
- if not detected_docs:
158
- detected_docs = ["ALL"]
159
-
160
  for doc in detected_docs:
161
  if doc not in parsed: parsed[doc] = []
162
  parsed[doc].append(final_text)
163
-
164
  return parsed
165
 
166
  def check_files(self, files, manual_cabinet):
 
167
  if not files: return "Файлы не загр��жены", None
168
  if self.excel_db.empty: return "Сначала загрузите Excel базу!", None
169
 
@@ -176,10 +171,13 @@ class KDChecker:
176
  detected_cabinet = manual_cabinet
177
  found_by_method = "manual"
178
  is_manual = True
 
179
  else:
180
  all_pdf_text = ""
181
  for file_path in files:
182
  all_pdf_text += self.extract_text(file_path) + "\n"
 
 
183
 
184
  pdf_numbers = self.find_all_decimal_numbers(all_pdf_text)
185
  db_clean_keys = set(self.excel_db["Cabinet_Clean"].tolist())
@@ -189,7 +187,7 @@ class KDChecker:
189
  detected_cabinet = cand
190
  found_by_method = "number"
191
  break
192
-
193
  if detected_cabinet == "Не определен":
194
  unique_cabinets = self.excel_db["Cabinet"].unique()
195
  for cab_name in unique_cabinets:
@@ -215,11 +213,9 @@ class KDChecker:
215
  for file_path in files:
216
  fname = os.path.basename(file_path)
217
  dtype = self.determine_doc_type(fname)
218
-
219
  tasks = []
220
  if dtype in remarks: tasks.extend(remarks[dtype])
221
  if "ALL" in remarks and dtype != "С2": tasks.extend(remarks["ALL"])
222
-
223
  if tasks:
224
  checklist[fname] = list(dict.fromkeys(tasks))
225
  processed_count += 1
@@ -227,11 +223,16 @@ class KDChecker:
227
  pdf_title = detected_cabinet
228
  if is_manual: pdf_title += " (Выбор вручную)"
229
 
230
- pdf = self.create_pdf(pdf_title, checklist)
231
- total = sum(len(v) for v in checklist.values())
 
 
 
 
 
232
 
233
- method_str = "Ручной выбор" if is_manual else (
234
- "По децимальному номеру" if is_clean_search else "По наименованию")
235
 
236
  return f"✅ Готово!\n\n📂 Шкаф: {detected_cabinet}\n🔍 Метод: {method_str}\n📄 Обработано файлов: {processed_count}\n🚩 Всего замечаний: {total}", pdf
237
 
@@ -242,28 +243,51 @@ class KDChecker:
242
  form = c.acroForm
243
  width, height = A4
244
 
245
- font_name = 'Helvetica'
246
- font_path = "arial.ttf"
247
-
248
- possible_paths = ["C:\\Windows\\Fonts\\arial.ttf", "arial.ttf",
249
- "/usr/share/fonts/truetype/msttcorefonts/Arial.ttf"]
250
 
251
- for p in possible_paths:
252
- if os.path.exists(p):
253
- try:
254
- pdfmetrics.registerFont(TTFont('Arial', p))
255
- font_name = 'Arial'
256
- break
257
- except:
258
- continue
 
 
 
 
 
 
 
 
 
259
 
260
  y = height - 50
261
- c.setFont(font_name, 16)
262
- c.drawString(50, y, f"ЧЕК-ЛИСТ ПРОВЕРКИ КД")
 
 
 
 
 
 
 
 
263
  y -= 25
264
  c.setFont(font_name, 12)
265
  disp_cab = cabinet[:60] + "..." if len(cabinet) > 60 else cabinet
266
- c.drawString(50, y, f"Шкаф: {disp_cab}")
 
 
 
 
 
 
 
 
267
  c.drawString(400, y, f"Дата: {datetime.now().strftime('%d.%m.%Y')}")
268
  y -= 20
269
  c.line(50, y, width - 50, y)
@@ -280,23 +304,25 @@ class KDChecker:
280
 
281
  c.setFillColor(colors.darkblue)
282
  c.setFont(font_name, 11)
283
- c.drawString(50, y, f"Файл: {filename}")
 
 
 
 
 
 
284
  c.setFillColor(colors.black)
285
  y -= 15
286
  c.setFont(font_name, 10)
287
 
288
  for task in tasks:
289
  paragraphs = task.split('\n')
290
-
291
  if y < 80:
292
- c.showPage();
293
- y = height - 50;
294
- c.setFont(font_name, 10)
295
 
296
  c.rect(50, y - 10, 10, 10)
297
  form.checkbox(name=f"cb_{cb_id}", x=50, y=y - 10, size=10, buttonStyle='check', forceBorder=True)
298
  cb_id += 1
299
-
300
  text_start_y = y - 2
301
 
302
  for paragraph in paragraphs:
@@ -313,14 +339,15 @@ class KDChecker:
313
 
314
  for l in lines:
315
  if text_start_y < 40:
316
- c.showPage();
317
- text_start_y = height - 50;
318
- c.setFont(font_name, 10)
319
- c.drawString(65, text_start_y, l.strip())
 
 
 
320
  text_start_y -= 12
321
-
322
  y = text_start_y - 8
323
-
324
  y -= 10
325
  c.setStrokeColor(colors.lightgrey)
326
  c.line(50, y, width - 50, y)
@@ -330,7 +357,6 @@ class KDChecker:
330
  c.save()
331
  return path
332
 
333
-
334
  def create_app():
335
  checker = KDChecker()
336
  with gr.Blocks(title="Генератор чек-листов КД") as app:
@@ -340,16 +366,15 @@ def create_app():
340
  with gr.Row():
341
  with gr.Column():
342
  gr.Markdown("### 1. База знаний")
343
- # ВАЖНО: Добавлен type="filepath", чтобы не зависало
344
  db_in = gr.File(label="Загрузить Excel (.xlsx)", type="filepath")
345
  manual_cab = gr.Dropdown(label="Или выберите шкаф-аналог вручную", choices=[], interactive=True)
346
  db_out = gr.Textbox(label="Статус загрузки", lines=8, max_lines=30)
347
-
348
  db_in.upload(checker.load_excel_db, inputs=[db_in], outputs=[db_out, manual_cab])
349
 
350
  with gr.Column():
351
  gr.Markdown("### 2. Документация (PDF)")
352
- # ВАЖНО: Добавлен type="filepath", чтобы не зависало
353
  files_in = gr.File(label="Загрузить чертежи", file_count="multiple", type="filepath")
354
  btn = gr.Button("Сформировать чек-лист", variant="primary")
355
 
@@ -360,7 +385,6 @@ def create_app():
360
  btn.click(checker.check_files, inputs=[files_in, manual_cab], outputs=[res_txt, res_pdf])
361
  return app
362
 
363
-
364
  app = create_app()
365
 
366
  if __name__ == "__main__":
 
11
  from reportlab.pdfbase.ttfonts import TTFont
12
  from reportlab.lib import colors
13
 
14
+ # --- ОТЛАДОЧНАЯ ПЕЧАТЬ ---
15
+ def debug_log(message):
16
+ print(f"[{datetime.now().strftime('%H:%M:%S')}] {message}")
17
+
18
  class KDChecker:
19
  def __init__(self):
20
  self.excel_db = pd.DataFrame()
 
22
  self.known_docs = ["Э3", "В4", "ПЭ3", "ВО", "ТЭ5", "СБ", "С5", "ОЛ", "Э1", "Э4", "Э7", "Д3", "Э6"]
23
 
24
  def load_excel_db(self, excel_path):
25
+ debug_log(f"Начало загрузки Excel: {excel_path}")
26
  if excel_path is None:
27
  return "Файл не выбран", gr.update(choices=[], value=None)
28
 
 
30
  sheets_log = []
31
 
32
  try:
 
33
  xls = pd.read_excel(excel_path, sheet_name=None, header=None)
34
+ debug_log(f"Excel прочитан, листов: {len(xls)}")
35
 
36
  for sheet_name, df_raw in xls.items():
37
  header_row_index = -1
 
69
  sheets_log.append(f"Лист '{sheet_name}': заголовки не найдены")
70
 
71
  if not all_data:
72
+ debug_log("Ошибка: данные не найдены")
73
  return "❌ Ошибка: Не найдены заголовки 'Шкаф' и 'Примечание'.", gr.update(choices=[], value=None)
74
 
75
  self.excel_db = pd.concat(all_data, ignore_index=True)
76
  self.cabinet_list = sorted(self.excel_db["Cabinet"].unique().tolist())
77
 
78
+ debug_log(f"Успех. Всего строк: {len(self.excel_db)}")
79
  msg = f"✅ База знаний загружена успешно!\nВсего записей: {len(self.excel_db)}\nОбработаны листы: {', '.join(sheets_log)}"
80
  return msg, gr.update(choices=self.cabinet_list, value=None, interactive=True)
81
 
82
  except Exception as e:
83
+ debug_log(f"Exception Excel: {e}")
84
+ return f"❌ Ошибка чтения Excel: {e}", gr.update(choices=[], value=None)
85
 
86
  def extract_text(self, pdf_path):
87
  try:
 
90
  for page in pdf.pages:
91
  full_text += (page.extract_text() or "") + "\n"
92
  return full_text
93
+ except Exception as e:
94
+ debug_log(f"Ошибка чтения PDF {pdf_path}: {e}")
95
  return ""
96
 
97
  def find_all_decimal_numbers(self, text):
 
122
 
123
  def get_remarks(self, cabinet_key, is_clean_key=True):
124
  if self.excel_db.empty: return {}
 
125
  if is_clean_key:
126
  target = cabinet_key.replace(" ", "")
127
  mask = self.excel_db['Cabinet_Clean'].str.contains(re.escape(target), case=False, na=False)
 
130
 
131
  rows = self.excel_db[mask]
132
  if rows.empty: return {}
 
133
  parsed = {}
 
134
  for remark_cell in rows['Remark']:
135
  cell_text = str(remark_cell)
136
  cell_text = re.sub(r'(\d+)\.([А-ЯA-Z])', r'\1. \2', cell_text)
 
137
  items = re.split(r'(?:^|\n)\s*(?=\d+[\.\)])', cell_text)
 
138
  for item in items:
139
  if len(item) < 3: continue
140
  clean_item = item.strip()
141
  clean_item_no_num = re.sub(r'^\d+[\.\)]\s*', '', clean_item)
 
142
  doc_pattern = r'^(?:Документ\s+|В\s+)?([А-ЯA-Z0-9\s,\(\)\-]+?)(?:[\.\:\-]|\s+)(.*)'
143
  match = re.match(doc_pattern, clean_item_no_num, re.IGNORECASE | re.DOTALL)
 
144
  detected_docs = []
145
  final_text = clean_item
 
146
  if match:
147
  potential_docs_str = match.group(1).upper()
148
  cleaned_codes = potential_docs_str.replace("(", " ").replace(")", " ").replace(",", " ")
149
  parts = cleaned_codes.split()
 
150
  valid_parts = [p for p in parts if p in self.known_docs]
151
  if valid_parts:
152
  detected_docs = valid_parts
153
  final_text = match.group(2).strip()
154
+ if not detected_docs: detected_docs = ["ALL"]
 
 
 
155
  for doc in detected_docs:
156
  if doc not in parsed: parsed[doc] = []
157
  parsed[doc].append(final_text)
 
158
  return parsed
159
 
160
  def check_files(self, files, manual_cabinet):
161
+ debug_log("Начало проверки файлов")
162
  if not files: return "Файлы не загр��жены", None
163
  if self.excel_db.empty: return "Сначала загрузите Excel базу!", None
164
 
 
171
  detected_cabinet = manual_cabinet
172
  found_by_method = "manual"
173
  is_manual = True
174
+ debug_log(f"Выбран ручной шкаф: {detected_cabinet}")
175
  else:
176
  all_pdf_text = ""
177
  for file_path in files:
178
  all_pdf_text += self.extract_text(file_path) + "\n"
179
+
180
+ debug_log(f"Текст извлечен, длина: {len(all_pdf_text)}")
181
 
182
  pdf_numbers = self.find_all_decimal_numbers(all_pdf_text)
183
  db_clean_keys = set(self.excel_db["Cabinet_Clean"].tolist())
 
187
  detected_cabinet = cand
188
  found_by_method = "number"
189
  break
190
+
191
  if detected_cabinet == "Не определен":
192
  unique_cabinets = self.excel_db["Cabinet"].unique()
193
  for cab_name in unique_cabinets:
 
213
  for file_path in files:
214
  fname = os.path.basename(file_path)
215
  dtype = self.determine_doc_type(fname)
 
216
  tasks = []
217
  if dtype in remarks: tasks.extend(remarks[dtype])
218
  if "ALL" in remarks and dtype != "С2": tasks.extend(remarks["ALL"])
 
219
  if tasks:
220
  checklist[fname] = list(dict.fromkeys(tasks))
221
  processed_count += 1
 
223
  pdf_title = detected_cabinet
224
  if is_manual: pdf_title += " (Выбор вручную)"
225
 
226
+ debug_log("Формирование PDF...")
227
+ try:
228
+ pdf = self.create_pdf(pdf_title, checklist)
229
+ debug_log("PDF успешно создан")
230
+ except Exception as e:
231
+ debug_log(f"ОШИБКА создания PDF: {e}")
232
+ return f"❌ Ошибка создания PDF (возможно, нет шрифта arial.ttf?): {e}", None
233
 
234
+ total = sum(len(v) for v in checklist.values())
235
+ method_str = "Ручной выбор" if is_manual else ("По децимальному номеру" if is_clean_search else "По наименованию")
236
 
237
  return f"✅ Готово!\n\n📂 Шкаф: {detected_cabinet}\n🔍 Метод: {method_str}\n📄 Обработано файлов: {processed_count}\n🚩 Всего замечаний: {total}", pdf
238
 
 
243
  form = c.acroForm
244
  width, height = A4
245
 
246
+ # --- ПОИСК ШРИФТА ---
247
+ font_name = 'Helvetica' # По умолчанию (но не умеет кириллицу)
248
+ font_path = "arial.ttf" # Ищем в текущей папке
 
 
249
 
250
+ if os.path.exists(font_path):
251
+ try:
252
+ pdfmetrics.registerFont(TTFont('Arial', font_path))
253
+ font_name = 'Arial'
254
+ except Exception as e:
255
+ debug_log(f"Ошибка регистрации шрифта: {e}")
256
+ else:
257
+ debug_log("⚠️ Файл arial.ttf не найден! Кирилица может сломаться.")
258
+ # Попытаемся найти системный, если локального нет (на HF не сработает, но для локалки полезно)
259
+ possible_paths = ["/usr/share/fonts/truetype/msttcorefonts/Arial.ttf"]
260
+ for p in possible_paths:
261
+ if os.path.exists(p):
262
+ try:
263
+ pdfmetrics.registerFont(TTFont('Arial', p))
264
+ font_name = 'Arial'
265
+ break
266
+ except: continue
267
 
268
  y = height - 50
269
+
270
+ # Если шрифт не Arial, кириллица вызовет ошибку. Пробуем перехватить.
271
+ try:
272
+ c.setFont(font_name, 16)
273
+ c.drawString(50, y, f"ЧЕК-ЛИСТ ПРОВЕРКИ КД")
274
+ except:
275
+ # Если упало на кирилли��е, пишем транслитом
276
+ c.setFont("Helvetica", 16)
277
+ c.drawString(50, y, "CHECK-LIST PROVERKI KD (Font Missing)")
278
+
279
  y -= 25
280
  c.setFont(font_name, 12)
281
  disp_cab = cabinet[:60] + "..." if len(cabinet) > 60 else cabinet
282
+
283
+ # Безопасный вывод текста
284
+ try:
285
+ c.drawString(50, y, f"Шкаф: {disp_cab}")
286
+ except:
287
+ c.setFont("Helvetica", 12)
288
+ c.drawString(50, y, f"Cabinet: {disp_cab} (Cyrillic Error)")
289
+ c.setFont(font_name, 12) # Возвращаем
290
+
291
  c.drawString(400, y, f"Дата: {datetime.now().strftime('%d.%m.%Y')}")
292
  y -= 20
293
  c.line(50, y, width - 50, y)
 
304
 
305
  c.setFillColor(colors.darkblue)
306
  c.setFont(font_name, 11)
307
+ try:
308
+ c.drawString(50, y, f"Файл: {filename}")
309
+ except:
310
+ c.setFont("Helvetica", 11)
311
+ c.drawString(50, y, f"File: {filename}")
312
+ c.setFont(font_name, 11)
313
+
314
  c.setFillColor(colors.black)
315
  y -= 15
316
  c.setFont(font_name, 10)
317
 
318
  for task in tasks:
319
  paragraphs = task.split('\n')
 
320
  if y < 80:
321
+ c.showPage(); y = height - 50; c.setFont(font_name, 10)
 
 
322
 
323
  c.rect(50, y - 10, 10, 10)
324
  form.checkbox(name=f"cb_{cb_id}", x=50, y=y - 10, size=10, buttonStyle='check', forceBorder=True)
325
  cb_id += 1
 
326
  text_start_y = y - 2
327
 
328
  for paragraph in paragraphs:
 
339
 
340
  for l in lines:
341
  if text_start_y < 40:
342
+ c.showPage(); text_start_y = height - 50; c.setFont(font_name, 10)
343
+ try:
344
+ c.drawString(65, text_start_y, l.strip())
345
+ except:
346
+ c.setFont("Helvetica", 10)
347
+ c.drawString(65, text_start_y, "Error printing line (font missing)")
348
+ c.setFont(font_name, 10)
349
  text_start_y -= 12
 
350
  y = text_start_y - 8
 
351
  y -= 10
352
  c.setStrokeColor(colors.lightgrey)
353
  c.line(50, y, width - 50, y)
 
357
  c.save()
358
  return path
359
 
 
360
  def create_app():
361
  checker = KDChecker()
362
  with gr.Blocks(title="Генератор чек-листов КД") as app:
 
366
  with gr.Row():
367
  with gr.Column():
368
  gr.Markdown("### 1. База знаний")
369
+ # type="filepath" чтобы не зависало
370
  db_in = gr.File(label="Загрузить Excel (.xlsx)", type="filepath")
371
  manual_cab = gr.Dropdown(label="Или выберите шкаф-аналог вручную", choices=[], interactive=True)
372
  db_out = gr.Textbox(label="Статус загрузки", lines=8, max_lines=30)
 
373
  db_in.upload(checker.load_excel_db, inputs=[db_in], outputs=[db_out, manual_cab])
374
 
375
  with gr.Column():
376
  gr.Markdown("### 2. Документация (PDF)")
377
+ # type="filepath" чтобы не зависало
378
  files_in = gr.File(label="Загрузить чертежи", file_count="multiple", type="filepath")
379
  btn = gr.Button("Сформировать чек-лист", variant="primary")
380
 
 
385
  btn.click(checker.check_files, inputs=[files_in, manual_cab], outputs=[res_txt, res_pdf])
386
  return app
387
 
 
388
  app = create_app()
389
 
390
  if __name__ == "__main__":