Skip to main content
Glama
list_handler.py10.5 kB
"""List handler for list and numbering operations. This module provides functionality for creating and managing bullet lists, numbered lists, and multi-level lists. """ from typing import Any, Optional from src.core.enums import ListType, NumberingFormat from src.core.exceptions import ValidationError from src.models.dto import ListDTO, ListItemDTO class ListHandler: """Handler for list operations. This class provides methods for creating and managing various types of lists in DOCX documents. """ def __init__(self, document: Optional[Any] = None) -> None: """Initialize the list handler. Args: document: The Document instance to work with (optional). """ self._document = document @property def document(self) -> Any: """Get the document instance.""" if self._document is None: raise ValueError("No document loaded") return self._document def set_document(self, document: Any) -> None: """Set the document instance. Args: document: The Document instance to work with. """ self._document = document def create_bullet_list( self, items: list[str], paragraph_index: Optional[int] = None, ) -> int: """Create a bullet list. Args: items: List of text items. paragraph_index: Optional starting paragraph index. Returns: Index of the first list paragraph. """ start_index = ( paragraph_index if paragraph_index is not None else len(self._document.paragraphs) ) for _i, item in enumerate(items): self._document.add_paragraph(item, style="List Bullet") return start_index def create_numbered_list( self, items: list[str], numbering_format: NumberingFormat = NumberingFormat.DECIMAL, paragraph_index: int | None = None, ) -> int: """Create a numbered list. Args: items: List of text items. numbering_format: Numbering format to use. paragraph_index: Optional starting paragraph index. Returns: Index of the first list paragraph. """ start_index = ( paragraph_index if paragraph_index is not None else len(self._document.paragraphs) ) for item in items: self._document.add_paragraph(item, style="List Number") return start_index def create_multilevel_list( self, items: list[tuple[str, int]], paragraph_index: int | None = None, ) -> int: """Create a multi-level list. Args: items: List of tuples (text, level). paragraph_index: Optional starting paragraph index. Returns: Index of the first list paragraph. """ start_index = ( paragraph_index if paragraph_index is not None else len(self._document.paragraphs) ) for text, level in items: if level == 0: style = "List Bullet" elif level == 1: style = "List Bullet 2" else: style = "List Bullet 3" self._document.add_paragraph(text, style=style) return start_index def add_list_item( self, text: str, list_type: ListType = ListType.BULLET, level: int = 0, ) -> int: """Add a single list item. Args: text: Item text. list_type: Type of list. level: Indentation level (0-8). Returns: Index of the new paragraph. """ if level < 0 or level > 8: raise ValidationError("List level must be between 0 and 8") if list_type == ListType.BULLET: style = f"List Bullet{' ' + str(level + 1) if level > 0 else ''}" else: style = f"List Number{' ' + str(level + 1) if level > 0 else ''}" try: self._document.add_paragraph(text, style=style) except KeyError: # Fall back to basic list style if level-specific style doesn't exist if list_type == ListType.BULLET: self._document.add_paragraph(text, style="List Bullet") else: self._document.add_paragraph(text, style="List Number") return len(self._document.paragraphs) - 1 def convert_to_list( self, paragraph_index: int, list_type: ListType = ListType.BULLET, ) -> None: """Convert a paragraph to a list item. Args: paragraph_index: Index of the paragraph to convert. list_type: Type of list. Raises: ValidationError: If the index is out of range. """ if paragraph_index < 0 or paragraph_index >= len(self._document.paragraphs): raise ValidationError(f"Paragraph index {paragraph_index} out of range") para = self._document.paragraphs[paragraph_index] if list_type == ListType.BULLET: para.style = "List Bullet" else: para.style = "List Number" def remove_list_formatting(self, paragraph_index: int) -> None: """Remove list formatting from a paragraph. Args: paragraph_index: Index of the paragraph. Raises: ValidationError: If the index is out of range. """ if paragraph_index < 0 or paragraph_index >= len(self._document.paragraphs): raise ValidationError(f"Paragraph index {paragraph_index} out of range") para = self._document.paragraphs[paragraph_index] para.style = "Normal" def get_list_items( self, start_index: int, end_index: int | None = None, ) -> ListDTO: """Get list items from a range of paragraphs. Args: start_index: Starting paragraph index. end_index: Ending paragraph index (exclusive). Returns: List DTO with items. Raises: ValidationError: If indices are out of range. """ if start_index < 0 or start_index >= len(self._document.paragraphs): raise ValidationError(f"Start index {start_index} out of range") if end_index is None: end_index = len(self._document.paragraphs) items = [] list_type = ListType.BULLET for i in range(start_index, min(end_index, len(self._document.paragraphs))): para = self._document.paragraphs[i] style_name = para.style.name if para.style else "" if "List" not in style_name: break if "Number" in style_name: list_type = ListType.NUMBERED # Determine level from style name level = 0 if style_name.endswith("2"): level = 1 elif style_name.endswith("3"): level = 2 items.append( ListItemDTO( index=i - start_index, text=para.text, level=level, ) ) return ListDTO( paragraph_index=start_index, list_type=list_type, items=items, ) def change_list_type( self, paragraph_index: int, list_type: ListType, ) -> None: """Change the type of a list item. Args: paragraph_index: Index of the list paragraph. list_type: New list type. Raises: ValidationError: If the index is out of range. """ if paragraph_index < 0 or paragraph_index >= len(self._document.paragraphs): raise ValidationError(f"Paragraph index {paragraph_index} out of range") para = self._document.paragraphs[paragraph_index] current_style = para.style.name if para.style else "" # Determine current level level = "" if current_style.endswith("2"): level = " 2" elif current_style.endswith("3"): level = " 3" if list_type == ListType.BULLET: para.style = f"List Bullet{level}" else: para.style = f"List Number{level}" def indent_list_item(self, paragraph_index: int) -> None: """Increase the indentation level of a list item. Args: paragraph_index: Index of the list paragraph. Raises: ValidationError: If the index is out of range. """ if paragraph_index < 0 or paragraph_index >= len(self._document.paragraphs): raise ValidationError(f"Paragraph index {paragraph_index} out of range") para = self._document.paragraphs[paragraph_index] style_name = para.style.name if para.style else "" if "List Bullet" in style_name: if style_name == "List Bullet": para.style = "List Bullet 2" elif style_name == "List Bullet 2": para.style = "List Bullet 3" elif "List Number" in style_name: if style_name == "List Number": para.style = "List Number 2" elif style_name == "List Number 2": para.style = "List Number 3" def outdent_list_item(self, paragraph_index: int) -> None: """Decrease the indentation level of a list item. Args: paragraph_index: Index of the list paragraph. Raises: ValidationError: If the index is out of range. """ if paragraph_index < 0 or paragraph_index >= len(self._document.paragraphs): raise ValidationError(f"Paragraph index {paragraph_index} out of range") para = self._document.paragraphs[paragraph_index] style_name = para.style.name if para.style else "" if "List Bullet" in style_name: if style_name == "List Bullet 3": para.style = "List Bullet 2" elif style_name == "List Bullet 2": para.style = "List Bullet" elif "List Number" in style_name: if style_name == "List Number 3": para.style = "List Number 2" elif style_name == "List Number 2": para.style = "List Number"

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Fu-Jie/MCP-OPENAPI-DOCX'

If you have feedback or need assistance with the MCP directory API, please join our Discord server