/
O.S.Prog
/
InstrumentProtocol
Обзор
Документация
Войти
/
O.S.Prog
/
InstrumentProtocol
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
core/protocol_manager.py
324 строки
13 KB
O.S.Prog
Initial commit: InstrumentProtocol v2.0
17 июл 2026, 15:11
17 июл 2026, 15:11
1e845bd
Код
Авторство
О чём код?
import os import re import tempfile from datetime import datetime from utils.file_utils import get_protocols_folder_for_type from utils.date_utils import generate_protocol_id from core.cache_manager import CacheManager from core.encryption_manager import EncryptionManager class ProtocolManager: def __init__(self, encryption_key=None): self.cache = CacheManager() self.encryption = EncryptionManager(encryption_key) if encryption_key else None def set_encryption_key(self, key): """Устанавливает ключ шифрования (после входа пользователя)""" self.encryption = EncryptionManager(key) def _get_folder(self, research_type): """Папка для протоколов конкретного типа""" return get_protocols_folder_for_type(research_type) def save_protocol(self, fio, birth_date, hospital, research_type, description, conclusion, exam_date, exam_time, existing_filepath=None, doctor_name=""): """Сохраняет протокол. Если existing_filepath — обновляет существующий.""" try: folder = self._get_folder(research_type) if existing_filepath: protocol_id = self._extract_id_from_filename(existing_filepath) if not protocol_id: protocol_id = generate_protocol_id() filepath = existing_filepath else: protocol_id = generate_protocol_id() filename = f"{fio.replace(' ', '_')}_{protocol_id}.docx" filepath = os.path.join(folder, filename) # Собираем DOCX from docx import Document from docx.shared import Pt from docx.enum.text import WD_ALIGN_PARAGRAPH doc = Document() # Учреждение hospital_para = doc.add_paragraph() hospital_run = hospital_para.add_run(hospital if hospital else "ЛЕЧЕБНОЕ УЧРЕЖДЕНИЕ") hospital_run.bold = True hospital_run.font.size = Pt(14) hospital_para.alignment = WD_ALIGN_PARAGRAPH.CENTER # Заголовок протокола title_para = doc.add_paragraph() title_run = title_para.add_run(f"ПРОТОКОЛ {research_type.upper()} ИССЛЕДОВАНИЯ") title_run.bold = True title_run.font.size = Pt(16) title_para.alignment = WD_ALIGN_PARAGRAPH.CENTER doc.add_paragraph() # Дата и время dt_para = doc.add_paragraph() dt_run = dt_para.add_run(f"Дата исследования: {exam_date} Время исследования: {exam_time}") dt_run.font.size = Pt(14) doc.add_paragraph() # Пациент patient_para = doc.add_paragraph() patient_run = patient_para.add_run(f"ФИО пациента: {fio}") patient_run.font.size = Pt(14) if birth_date: patient_para.add_run(f" Дата рождения пациента: {birth_date}").font.size = Pt(14) doc.add_paragraph() # Описание if description.strip(): desc_label = doc.add_paragraph() desc_label.add_run("ОПИСАНИЕ:").bold = True desc_label.runs[0].font.size = Pt(14) for paragraph in description.split('\n'): p = doc.add_paragraph() p.add_run(paragraph).font.size = Pt(14) # Заключение if conclusion.strip(): doc.add_paragraph() concl_label = doc.add_paragraph() concl_label.add_run("ЗАКЛЮЧЕНИЕ:").bold = True concl_label.runs[0].font.size = Pt(14) for paragraph in conclusion.split('\n'): p = doc.add_paragraph() p.add_run(paragraph).font.size = Pt(14) doc.add_paragraph() # Подпись врача sign_para = doc.add_paragraph() sign_para.alignment = WD_ALIGN_PARAGRAPH.RIGHT if doctor_name: sign_run = sign_para.add_run(f"Врач: ________________________ ({doctor_name})") else: sign_run = sign_para.add_run("Врач: ________________________") sign_run.font.size = Pt(14) # Сохраняем DOCX во временный файл tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".docx") tmp.close() doc.save(tmp.name) # Шифруем и сохраняем как .enc if filepath.endswith('.enc'): enc_path = filepath else: enc_path = filepath + ".enc" if self.encryption: self.encryption.encrypt_file(tmp.name, enc_path) else: # Без шифрования — просто копируем import shutil shutil.copy2(tmp.name, enc_path) os.unlink(tmp.name) # Если это обновление — удаляем старый .enc файл if existing_filepath and existing_filepath != enc_path: if os.path.exists(existing_filepath): os.remove(existing_filepath) # Обновляем кэш self.cache.add(enc_path, { 'filepath': enc_path, 'filename': os.path.basename(enc_path), 'fio': fio, 'birth_date': birth_date, 'exam_date': exam_date, 'exam_time': exam_time, 'hospital': hospital, 'research_type': research_type, 'id': protocol_id, 'doctor_name': doctor_name }) return True, enc_path except Exception as e: print(f"Ошибка сохранения: {e}") return False, "" def load_protocol_text(self, filepath): """Загружает текст протокола из .enc файла. Возвращает (description, conclusion, doctor_name)""" try: tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".docx") tmp.close() if self.encryption: self.encryption.decrypt_file(filepath, tmp.name) else: import shutil shutil.copy2(filepath, tmp.name) from docx import Document doc = Document(tmp.name) # Читаем все параграфы сразу в список, пока файл ещё существует all_paragraphs = [para.text.strip() for para in doc.paragraphs] # Теперь можно удалить временный файл # os.unlink(tmp.name) hospital_from_file = all_paragraphs[0] if all_paragraphs else "" description = [] conclusion = [] doctor_name = "" current_section = "description" for text in all_paragraphs: doctor_match = re.search(r'Врач:.*\((.*?)\)', text) if doctor_match: doctor_name = doctor_match.group(1) if text == "ОПИСАНИЕ:": current_section = "description" continue elif text == "ЗАКЛЮЧЕНИЕ:": current_section = "conclusion" continue if text.startswith("ФИО пациента:") or text.startswith("Дата исследования:"): continue if text.startswith("ПРОТОКОЛ") or text.startswith("Врач:"): continue if not text: continue if text == hospital_from_file: continue if current_section == "description": description.append(text) else: conclusion.append(text) return '\n'.join(description), '\n'.join(conclusion), doctor_name except Exception as e: print(f"Ошибка загрузки: {e}") return "", "", "" def get_protocol_files(self, research_type): """Список .enc файлов в папке типа исследования""" folder = self._get_folder(research_type) if not os.path.exists(folder): return [] return [os.path.join(folder, f) for f in os.listdir(folder) if f.endswith('.enc')] def get_all_protocols(self, research_type=None): """Быстрый список протоколов (без открытия файлов). Если research_type=None — все типы.""" protocols = [] if research_type: folders = [(research_type, self._get_folder(research_type))] else: root = os.path.dirname(self._get_folder("")) if os.path.exists(root): folders = [(d, os.path.join(root, d)) for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))] else: folders = [] for rtype, folder in folders: if not os.path.exists(folder): continue for filename in os.listdir(folder): if filename.endswith('.enc'): filepath = os.path.join(folder, filename) info = self._extract_info_from_filename(filepath, filename, rtype) if info: protocols.append(info) protocols.sort(key=lambda x: x['id'], reverse=True) return protocols def _extract_info_from_filename(self, filepath, filename, research_type): """Извлекает базовую информацию из имени файла""" try: name_no_ext = filename.replace('.enc', '') parts = name_no_ext.split('_') if len(parts) >= 2: fio = ' '.join(parts[:-1]) protocol_id = parts[-1] else: fio = "Неизвестный" protocol_id = "0000.00.00.00.00" return { 'filepath': filepath, 'filename': filename, 'fio': fio, 'id': protocol_id, 'research_type': research_type, 'birth_date': '', 'exam_date': '', 'exam_time': '', 'hospital': '', 'doctor_name': '' } except: return None def delete_protocol(self, filepath): """Удаляет протокол""" try: if os.path.exists(filepath): os.remove(filepath) self.cache.remove(filepath) return True return False except Exception as e: print(f"Ошибка удаления: {e}") return False def _extract_id_from_filename(self, filepath): filename = os.path.basename(filepath) name_no_ext = filename.replace('.enc', '') parts = name_no_ext.split('_') if len(parts) >= 2: return parts[-1] return None def reencrypt_protocol(self, filepath, old_encryption, new_encryption): """Перешифровывает протокол с одного ключа на другой. Возвращает doctor_name.""" try: tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".docx") tmp.close() old_encryption.decrypt_file(filepath, tmp.name) # Читаем автора из расшифрованного файла from docx import Document import re doctor_name = "" try: doc = Document(tmp.name) for para in doc.paragraphs: match = re.search(r'Врач:.*\((.*?)\)', para.text) if match: doctor_name = match.group(1) break except: pass # Шифруем новым ключом new_encryption.encrypt_file(tmp.name, filepath) os.unlink(tmp.name) return True, doctor_name except Exception as e: print(f"Ошибка перешифровки: {e}") return False, ""