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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -60
app.py CHANGED
@@ -11,10 +11,6 @@ from reportlab.pdfbase import pdfmetrics
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,7 +18,6 @@ class KDChecker:
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,8 +25,8 @@ class KDChecker:
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
@@ -52,7 +47,7 @@ class KDChecker:
52
  break
53
 
54
  if header_row_index != -1:
55
- df = pd.read_excel(excel_path, sheet_name=sheet_name, header=header_row_index)
56
  df_subset = df.iloc[:, [cab_col_idx, rem_col_idx]]
57
  df_subset.columns = ["Cabinet", "Remark"]
58
 
@@ -69,19 +64,17 @@ class KDChecker:
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,8 +83,7 @@ class KDChecker:
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,6 +114,7 @@ class KDChecker:
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,35 +123,46 @@ class KDChecker:
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,13 +175,10 @@ class KDChecker:
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,7 +188,7 @@ class KDChecker:
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,9 +214,11 @@ class KDChecker:
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,16 +226,15 @@ class KDChecker:
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,50 +245,41 @@ class KDChecker:
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
@@ -307,9 +300,9 @@ class KDChecker:
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
@@ -317,12 +310,16 @@ class KDChecker:
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,15 +336,20 @@ class KDChecker:
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,6 +359,7 @@ class KDChecker:
357
  c.save()
358
  return path
359
 
 
360
  def create_app():
361
  checker = KDChecker()
362
  with gr.Blocks(title="Генератор чек-листов КД") as app:
@@ -366,15 +369,16 @@ def create_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,6 +389,7 @@ def create_app():
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__":
 
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
  self.known_docs = ["Э3", "В4", "ПЭ3", "ВО", "ТЭ5", "СБ", "С5", "ОЛ", "Э1", "Э4", "Э7", "Д3", "Э6"]
19
 
20
  def load_excel_db(self, excel_path):
 
21
  if excel_path is None:
22
  return "Файл не выбран", gr.update(choices=[], value=None)
23
 
 
25
  sheets_log = []
26
 
27
  try:
28
+ # --- ПРАВКА ДЛЯ СЕРВЕРА: явно указываем движок openpyxl ---
29
+ xls = pd.read_excel(excel_path, sheet_name=None, header=None, engine='openpyxl')
30
 
31
  for sheet_name, df_raw in xls.items():
32
  header_row_index = -1
 
47
  break
48
 
49
  if header_row_index != -1:
50
+ df = pd.read_excel(excel_path, sheet_name=sheet_name, header=header_row_index, engine='openpyxl')
51
  df_subset = df.iloc[:, [cab_col_idx, rem_col_idx]]
52
  df_subset.columns = ["Cabinet", "Remark"]
53
 
 
64
  sheets_log.append(f"Лист '{sheet_name}': заголовки не найдены")
65
 
66
  if not all_data:
 
67
  return "❌ Ошибка: Не найдены заголовки 'Шкаф' и 'Примечание'.", gr.update(choices=[], value=None)
68
 
69
  self.excel_db = pd.concat(all_data, ignore_index=True)
70
  self.cabinet_list = sorted(self.excel_db["Cabinet"].unique().tolist())
71
 
 
72
  msg = f"✅ База знаний загружена успешно!\nВсего записей: {len(self.excel_db)}\nОбработаны листы: {', '.join(sheets_log)}"
73
  return msg, gr.update(choices=self.cabinet_list, value=None, interactive=True)
74
 
75
  except Exception as e:
76
+ # Выводим ошибку прямо в интерфейс
77
+ return f"❌ КРИТИЧЕСКАЯ ОШИБКА EXCEL: {str(e)}", gr.update(choices=[], value=None)
78
 
79
  def extract_text(self, pdf_path):
80
  try:
 
83
  for page in pdf.pages:
84
  full_text += (page.extract_text() or "") + "\n"
85
  return full_text
86
+ except:
 
87
  return ""
88
 
89
  def find_all_decimal_numbers(self, text):
 
114
 
115
  def get_remarks(self, cabinet_key, is_clean_key=True):
116
  if self.excel_db.empty: return {}
117
+
118
  if is_clean_key:
119
  target = cabinet_key.replace(" ", "")
120
  mask = self.excel_db['Cabinet_Clean'].str.contains(re.escape(target), case=False, na=False)
 
123
 
124
  rows = self.excel_db[mask]
125
  if rows.empty: return {}
126
+
127
  parsed = {}
128
+
129
  for remark_cell in rows['Remark']:
130
  cell_text = str(remark_cell)
131
  cell_text = re.sub(r'(\d+)\.([А-ЯA-Z])', r'\1. \2', cell_text)
132
+
133
  items = re.split(r'(?:^|\n)\s*(?=\d+[\.\)])', cell_text)
134
+
135
  for item in items:
136
  if len(item) < 3: continue
137
  clean_item = item.strip()
138
  clean_item_no_num = re.sub(r'^\d+[\.\)]\s*', '', clean_item)
139
+
140
  doc_pattern = r'^(?:Документ\s+|В\s+)?([А-ЯA-Z0-9\s,\(\)\-]+?)(?:[\.\:\-]|\s+)(.*)'
141
  match = re.match(doc_pattern, clean_item_no_num, re.IGNORECASE | re.DOTALL)
142
+
143
  detected_docs = []
144
  final_text = clean_item
145
+
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
+
151
  valid_parts = [p for p in parts if p in self.known_docs]
152
  if valid_parts:
153
  detected_docs = valid_parts
154
  final_text = match.group(2).strip()
155
+
156
+ if not detected_docs:
157
+ detected_docs = ["ALL"]
158
+
159
  for doc in detected_docs:
160
  if doc not in parsed: parsed[doc] = []
161
  parsed[doc].append(final_text)
162
+
163
  return parsed
164
 
165
  def check_files(self, files, manual_cabinet):
 
166
  if not files: return "Файлы не загружены", None
167
  if self.excel_db.empty: return "Сначала загрузите Excel базу!", None
168
 
 
175
  detected_cabinet = manual_cabinet
176
  found_by_method = "manual"
177
  is_manual = True
 
178
  else:
179
  all_pdf_text = ""
180
  for file_path in files:
181
  all_pdf_text += self.extract_text(file_path) + "\n"
 
 
182
 
183
  pdf_numbers = self.find_all_decimal_numbers(all_pdf_text)
184
  db_clean_keys = set(self.excel_db["Cabinet_Clean"].tolist())
 
188
  detected_cabinet = cand
189
  found_by_method = "number"
190
  break
191
+
192
  if detected_cabinet == "Не определен":
193
  unique_cabinets = self.excel_db["Cabinet"].unique()
194
  for cab_name in unique_cabinets:
 
214
  for file_path in files:
215
  fname = os.path.basename(file_path)
216
  dtype = self.determine_doc_type(fname)
217
+
218
  tasks = []
219
  if dtype in remarks: tasks.extend(remarks[dtype])
220
  if "ALL" in remarks and dtype != "С2": tasks.extend(remarks["ALL"])
221
+
222
  if tasks:
223
  checklist[fname] = list(dict.fromkeys(tasks))
224
  processed_count += 1
 
226
  pdf_title = detected_cabinet
227
  if is_manual: pdf_title += " (Выбор вручную)"
228
 
229
+ # --- ВАЖНО: Проверка создания PDF ---
230
  try:
231
  pdf = self.create_pdf(pdf_title, checklist)
 
232
  except Exception as e:
233
+ return f" Ошибка создания PDF: {e}\n(Возможно, не загружен шрифт arial.ttf?)", None
 
234
 
235
  total = sum(len(v) for v in checklist.values())
236
+ method_str = "Ручной выбор" if is_manual else (
237
+ "По децимальному номеру" if is_clean_search else "По наименованию")
238
 
239
  return f"✅ Готово!\n\n📂 Шкаф: {detected_cabinet}\n🔍 Метод: {method_str}\n📄 Обработано файлов: {processed_count}\n🚩 Всего замечаний: {total}", pdf
240
 
 
245
  form = c.acroForm
246
  width, height = A4
247
 
248
+ # --- ПОИСК ШРИФТА ДЛЯ СЕРВЕРА ---
249
+ font_name = 'Helvetica'
250
+ font_path = "arial.ttf" # Файл должен лежать в Files на Hugging Face
251
 
252
  if os.path.exists(font_path):
253
  try:
254
  pdfmetrics.registerFont(TTFont('Arial', font_path))
255
  font_name = 'Arial'
256
+ except:
257
+ pass
258
+
259
+ # Если шрифт не найден, но мы на сервере - это проблема.
260
+ # Код попытается использовать Helvetica, но кириллица пропадет.
 
 
 
 
 
 
 
 
261
 
262
  y = height - 50
263
+ c.setFont(font_name, 16)
264
 
265
+ # Защита от ошибок кодировки при отсутствии шрифта
266
  try:
 
267
  c.drawString(50, y, f"ЧЕК-ЛИСТ ПРОВЕРКИ КД")
268
  except:
269
+ c.setFont("Helvetica", 16)
270
+ c.drawString(50, y, "CHECK-LIST (Font Error)")
271
+ c.setFont(font_name, 16)
272
 
273
  y -= 25
274
  c.setFont(font_name, 12)
275
  disp_cab = cabinet[:60] + "..." if len(cabinet) > 60 else cabinet
276
 
 
277
  try:
278
  c.drawString(50, y, f"Шкаф: {disp_cab}")
279
  except:
280
+ c.setFont("Helvetica", 12)
281
+ c.drawString(50, y, "Cabinet: (Name Error)")
282
+ c.setFont(font_name, 12)
283
 
284
  c.drawString(400, y, f"Дата: {datetime.now().strftime('%d.%m.%Y')}")
285
  y -= 20
 
300
  try:
301
  c.drawString(50, y, f"Файл: {filename}")
302
  except:
303
+ c.setFont("Helvetica", 11)
304
+ c.drawString(50, y, f"File: {filename}")
305
+ c.setFont(font_name, 11)
306
 
307
  c.setFillColor(colors.black)
308
  y -= 15
 
310
 
311
  for task in tasks:
312
  paragraphs = task.split('\n')
313
+
314
  if y < 80:
315
+ c.showPage();
316
+ y = height - 50;
317
+ c.setFont(font_name, 10)
318
 
319
  c.rect(50, y - 10, 10, 10)
320
  form.checkbox(name=f"cb_{cb_id}", x=50, y=y - 10, size=10, buttonStyle='check', forceBorder=True)
321
  cb_id += 1
322
+
323
  text_start_y = y - 2
324
 
325
  for paragraph in paragraphs:
 
336
 
337
  for l in lines:
338
  if text_start_y < 40:
339
+ c.showPage();
340
+ text_start_y = height - 50;
341
+ c.setFont(font_name, 10)
342
+
343
  try:
344
  c.drawString(65, text_start_y, l.strip())
345
  except:
346
+ # Игнорируем строки, которые нельзя напечатать без шрифта
347
+ pass
348
+
349
  text_start_y -= 12
350
+
351
  y = text_start_y - 8
352
+
353
  y -= 10
354
  c.setStrokeColor(colors.lightgrey)
355
  c.line(50, y, width - 50, y)
 
359
  c.save()
360
  return path
361
 
362
+
363
  def create_app():
364
  checker = KDChecker()
365
  with gr.Blocks(title="Генератор чек-листов КД") as app:
 
369
  with gr.Row():
370
  with gr.Column():
371
  gr.Markdown("### 1. База знаний")
372
+ # type="filepath" ОБЯЗАТЕЛЬНО ДЛЯ СЕРВЕРА
373
  db_in = gr.File(label="Загрузить Excel (.xlsx)", type="filepath")
374
  manual_cab = gr.Dropdown(label="Или выберите шкаф-аналог вручную", choices=[], interactive=True)
375
  db_out = gr.Textbox(label="Статус загрузки", lines=8, max_lines=30)
376
+
377
  db_in.upload(checker.load_excel_db, inputs=[db_in], outputs=[db_out, manual_cab])
378
 
379
  with gr.Column():
380
  gr.Markdown("### 2. Документация (PDF)")
381
+ # type="filepath" ОБЯЗАТЕЛЬНО ДЛЯ СЕРВЕРА
382
  files_in = gr.File(label="Загрузить чертежи", file_count="multiple", type="filepath")
383
  btn = gr.Button("Сформировать чек-лист", variant="primary")
384
 
 
389
  btn.click(checker.check_files, inputs=[files_in, manual_cab], outputs=[res_txt, res_pdf])
390
  return app
391
 
392
+
393
  app = create_app()
394
 
395
  if __name__ == "__main__":