# -*- coding: utf-8 -*-"""Confluence DocPack Core ModuleHigh-level API for exporting Confluence pages from multiple spacesto XML format for AI knowledge base ingestion."""importdataclassesimportshutilfrompathlibimportPathfromsanhe_confluence_sdk.apiimportConfluencefrom.constantsimportConfluencePageFieldEnumfrom.constantsimportDescendantTypeEnumfrom.constantsimportBreadCrumbTypeEnumfrom.shortcutsimportget_space_by_idfrom.shortcutsimportget_space_by_keyfrom.shortcutsimportget_pages_by_idsfrom.crawlerimportselect_entitiesfrom.pageimportPagefrom.exporterimportexport_pages_to_xml_files,merge_files
[docs]@dataclasses.dataclass(frozen=True)classSpaceExportConfig:""" Configuration for exporting pages from a single Confluence space. :param client: Confluence API client :param space_id: Space ID (mutually exclusive with space_key) :param space_key: Space key (mutually exclusive with space_id) :param include: Glob patterns for pages to include :param exclude: Glob patterns for pages to exclude :param breadcrumb_type: Filename format - use page IDs or titles :param wanted_fields: Fields to include in XML output :param ignore_to_markdown_error: Skip errors during markdown conversion """# fmt: offclient:Confluence=dataclasses.field()space_id:int|None=dataclasses.field(default=None)space_key:str|None=dataclasses.field(default=None)include:list[str]|None=dataclasses.field(default=None)exclude:list[str]|None=dataclasses.field(default=None)breadcrumb_type:BreadCrumbTypeEnum=dataclasses.field(default=BreadCrumbTypeEnum.title)wanted_fields:set[ConfluencePageFieldEnum]|None=dataclasses.field(default=None)ignore_to_markdown_error:bool=dataclasses.field(default=True)# fmt: on@propertydefspace_identifier(self)->str:"""Unique identifier string for output directory naming."""ifself.space_idisnotNone:returnf"space_id_{self.space_id}"elifself.space_keyisnotNone:returnf"space_key_{self.space_key}"else:raiseValueError("Either space_id or space_key must be provided")
[docs]defexport(self,dir_out:Path,encoding:str="utf-8")->None:""" Export filtered pages from this space to XML files. :param dir_out: Output directory for XML files :param encoding: Output file encoding """# Get homepage ID to start crawlingifself.space_idisnotNone:homepage_id=get_space_by_id(client=self.client,space_id=self.space_id).homepageIdelifself.space_keyisnotNone:homepage_id=get_space_by_key(client=self.client,space_key=self.space_key).homepageIdelse:raiseValueError("Either space_id or space_key must be provided")entities=select_entities(client=self.client,root_id=int(homepage_id),root_type=DescendantTypeEnum.page,include=self.include,exclude=self.exclude,verbose=False,)# Fetch full page contentresults=get_pages_by_ids(client=self.client,ids=[int(entity.node.id)forentityinentities],)iflen(entities)!=len(results):raiseValueError("Mismatch between filtered_entities and fetched_pages")result_by_id={str(result.id):resultforresultinresults}# Build Page objectspages=[Page(site_url=self.client.url,entity=entity,result=result_by_id[str(entity.node.id)],)forentityinentities]# Export to XML filesexport_pages_to_xml_files(pages=pages,dir_out=dir_out,breadcrumb_type=self.breadcrumb_type,wanted_fields=self.wanted_fields,ignore_to_markdown_error=self.ignore_to_markdown_error,encoding=encoding,clean_output_dir=False,)
[docs]@dataclasses.dataclass(frozen=True)classExportSpec:""" Specification for exporting pages from multiple Confluence spaces. :param space_configs: List of space export configurations :param dir_out: Root output directory :param encoding: File encoding for all output files """space_configs:list[SpaceExportConfig]=dataclasses.field()dir_out:Path=dataclasses.field()encoding:str=dataclasses.field(default="utf-8")@propertydefpath_merged_output(self)->Path:"""Path to merged knowledge base file."""returnself.dir_out/"all_in_one_knowledge_base.txt"
[docs]defexport(self)->None:""" Execute the export: crawl, filter, and export pages from all spaces, then merge into a single knowledge base file. """# Clean output directoryshutil.rmtree(self.dir_out,ignore_errors=True)# Export each space to its own subdirectorydir_out_list:list[Path]=[]forspace_configinself.space_configs:dir_out_space=self.dir_out/space_config.space_identifierdir_out_list.append(dir_out_space)space_config.export(dir_out=dir_out_space,encoding=self.encoding)# Merge all exported files into onemerge_files(dir_in_list=dir_out_list,path_out=self.path_merged_output,ext=".xml",input_encoding=self.encoding,output_encoding=self.encoding,)