import logging from typing import Dict, List from datetime import datetime try: from core.sensitive_data_guard import anonymizer except ImportError: try: from backend.core.sensitive_data_guard import anonymizer except ImportError: anonymizer = None logger = logging.getLogger(__name__) class DocumentBuilder: """ Formatyzer sk\u0142adaj\u0105cy cz\u0119\u015bci dokumentu z GeneratorAgent w sp\u00f3jny format Markdown gotowy do eksportu docx/pdf. Przywraca PII stosuj\u0105c Deanonimizator. """ @staticmethod def build_markdown( sections_plan: List[Dict[str, str] | str], generated_sections: Dict[str, str], document_type: str, project_title: str = "", company_name: str = "", traceability_data: Dict[str, List[dict]] = None, ) -> str: """ Scala sekcje wed\u0142ug ich oryginalnej kolejno\u015bci ze stanu i tworzy reprezentacj\u0119 Markdown. """ logger.info(f"Scalanie dokumentu: {document_type}") title = project_title or document_type md_lines = [f"# {title}", ""] if company_name: md_lines += [f"**Wnioskodawca:** {company_name}", ""] md_lines += [ f"**Typ dokumentu:** {document_type}", f"**Data wygenerowania:** {datetime.now().strftime('%d.%m.%Y %H:%M')}", "", "---", "", ] for section_def in sections_plan: section = ( section_def["title"] if isinstance(section_def, dict) else section_def ) content = generated_sections.get( section, "*(Sekcja nie została wygenerowana)*" ) md_lines.append(f"## {section}") md_lines.append(content) md_lines.append("") # odstęp # Załącznik: Źródła i dokumenty (Traceability) if traceability_data: md_lines.append("## Załącznik: Źródła i dokumenty") md_lines.append("Poniżej znajduje się lista dokumentów (regulaminów, wytycznych), na podstawie których sztuczna inteligencja wygenerowała poszczególne sekcje. Każdy dokument posiada unikalny skrót (Hash SHA-256) chroniący przed niezauważalnymi zmianami w przyszłości.") md_lines.append("") for sec_name, traces in traceability_data.items(): if traces: md_lines.append(f"### Sekcja: {sec_name}") for t in traces: md_lines.append(f"- **Źródło:** {t.get('source', 'Brak')}") ver_str = t.get('version_id') vf_str = t.get('valid_from') vt_str = t.get('valid_to') if ver_str or vf_str or vt_str: md_lines.append(f" - **Wersja dokumentu:** {ver_str or 'Nieznana'} (Obowiązuje od {vf_str or '-'} do {vt_str or '-'})") md_lines.append(f" - **Link:** {t.get('url', 'Brak')}") md_lines.append(f" - **Data pozyskania:** {t.get('date', 'Brak')}") md_lines.append(f" - **Hash (SHA-256):** `{t.get('hash', 'Brak')}`") md_lines.append("") # Stopka AI md_lines += [ "---", "", "cz\u0119\u015bciowo przy u\u017cyciu modeli j\u0119zykowych (AI). Tre\u015b\u0107 powinna zosta\u0107 " "zweryfikowana przez uprawnionego doradc\u0119 przed z\u0142o\u017ceniem wniosku. " "Wydawca nie ponosi odpowiedzialno\u015bci za b\u0142\u0119dy merytoryczne wygenerowanego tekstu.", "", ] full_text = "\n".join(md_lines) # Deanonimizacja PII (NIP, know-how, etc.) if anonymizer: try: full_text = anonymizer.deanonymize_text(full_text) except Exception as e: logger.warning(f"Deanonimizacja nie powiod\u0142a si\u0119: {e}") return full_text