# -*- coding: utf-8 -*-"""Confluence Page ExporterExport Confluence pages to XML format for AI knowledge base ingestion.The XML output contains structured metadata and markdown content optimizedfor LLM context injection."""importtypingasTimportshutilfrompathlibimportPathfrom.constantsimportBreadCrumbTypeEnum,ConfluencePageFieldEnumfrom.utilsimportsafe_writefrom.pageimportPage
[docs]defexport_pages_to_xml_files(pages:T.List[Page],dir_out:Path,breadcrumb_type:BreadCrumbTypeEnum=BreadCrumbTypeEnum.title,wanted_fields:T.Optional[T.Set[ConfluencePageFieldEnum]]=None,ignore_to_markdown_error:bool=True,encoding:str="utf-8",clean_output_dir:bool=False,)->None:""" Export Confluence pages to individual XML files. Each page is serialized to XML with metadata and markdown content, named by its breadcrumb path to preserve hierarchy. :param pages: Pages to export (from crawler) :param dir_out: Output directory for XML files :param breadcrumb_type: Filename format - use page IDs or titles :param wanted_fields: Fields to include in XML; None means all fields :param ignore_to_markdown_error: Skip errors during markdown conversion :param encoding: Output file encoding :param clean_output_dir: Remove output directory before export """ifclean_output_dir:shutil.rmtree(dir_out,ignore_errors=True)forpageinpages:# Convert page to XMLxml=page.to_xml(wanted_fields=wanted_fields,to_markdown_ignore_error=ignore_to_markdown_error,)# Determine filename from breadcrumb pathifbreadcrumb_type==BreadCrumbTypeEnum.id:basename=f"{page.entity.id_breadcrumb_path}.xml"elifbreadcrumb_type==BreadCrumbTypeEnum.title:basename=f"{page.entity.title_breadcrumb_path}.xml"else:# pragma: no coverraiseTypeError(f"Unsupported breadcrumb_type: {breadcrumb_type}")path=dir_out/basenamesafe_write(path=path,content=xml,encoding=encoding)
[docs]defmerge_files(dir_in_list:T.List[Path],path_out:Path,ext:str=".xml",input_encoding:str="utf-8",output_encoding:str="utf-8",overwrite:bool=True,)->None:""" Merge exported files into a single document. Collects all files with specified extension from input directories and concatenates them into one file for AI context ingestion. :param dir_in_list: Directories containing files to merge :param path_out: Output file path :param ext: File extension filter (e.g., ".xml") :param input_encoding: Input files encoding :param output_encoding: Output file encoding :param overwrite: If False, raise error when output exists :raises FileExistsError: If output exists and overwrite is False """ifnotoverwriteandpath_out.exists():raiseFileExistsError(f"File already exists: {path_out}")# Collect files from all input directoriespaths:T.List[Path]=[]fordir_inindir_in_list:paths.extend(dir_in.glob(f"**/*{ext}"))paths.sort()# Read and concatenatecontents=[p.read_text(encoding=input_encoding)forpinpaths]safe_write(path=path_out,content="\n".join(contents),encoding=output_encoding,)