Skip to content

auto md and excel module

General auto-generation module for converting markdown and Excel to Word documents.

AutoReportGenerator

Convert markdown files and Excel tables to Word document sections with proper formatting.

Source code in autorpt/gen_auto.py
class AutoReportGenerator:
    """Convert markdown files and Excel tables to Word document sections with proper formatting."""

    def __init__(self, document=None, content_folder="reports"):
        """Initialize the generator with an optional Word document.

        Args:
            document: Existing Word document object, or None to create new one
            content_folder (str): Folder to scan for additional content files
        """
        self.document = document if document else Document()
        self.content_folder = Path(content_folder)

    def discover_content_files(self, include_patterns=None, exclude_patterns=None):
        """Automatically discover markdown and Excel files in the content folder.

        Args:
            include_patterns (list): List of glob patterns to include (default: ['*.md', '*.xlsx', '*.xls'])
            exclude_patterns (list): List of patterns to exclude (default: ['*report*.docx'])

        Returns:
            dict: Dictionary with 'markdown' and 'excel' file lists
        """
        if include_patterns is None:
            include_patterns = ['*.md', '*.markdown', '*.xlsx', '*.xls']

        if exclude_patterns is None:
            exclude_patterns = ['*report*.docx', '*report*.pdf']

        discovered = {'markdown': [], 'excel': [], 'all': []}

        if not self.content_folder.exists():
            print(f"📁 Content folder '{self.content_folder}' not found")
            return discovered

        try:
            # Find all matching files
            all_files = []
            for pattern in include_patterns:
                all_files.extend(self.content_folder.glob(pattern))

            # Filter out excluded patterns
            filtered_files = []
            for file_path in all_files:
                exclude_file = False
                for exclude_pattern in exclude_patterns:
                    if file_path.match(exclude_pattern):
                        exclude_file = True
                        break
                if not exclude_file:
                    filtered_files.append(file_path)

            # Categorize files
            for file_path in filtered_files:
                if file_path.suffix.lower() in ['.md', '.markdown']:
                    discovered['markdown'].append(file_path)
                elif file_path.suffix.lower() in ['.xlsx', '.xls']:
                    discovered['excel'].append(file_path)
                discovered['all'].append(file_path)

            # Sort files by name for consistent ordering
            discovered['markdown'].sort(key=lambda x: x.name)
            discovered['excel'].sort(key=lambda x: x.name)
            discovered['all'].sort(key=lambda x: x.name)

            if discovered['all']:
                print(
                    f"📂 Discovered {len(discovered['all'])} content files in '{self.content_folder}':")
                for file_path in discovered['all']:
                    print(f"   📄 {file_path.name}")
            else:
                print(
                    f"📂 No additional content files found in '{self.content_folder}'")

        except (OSError, PermissionError) as e:
            print(f"❌ Error discovering content files: {e}")

        return discovered

    def parse_markdown_file(self, markdown_file_path):
        """Parse a markdown file and extract structured content.

        Args:
            markdown_file_path (str or Path): Path to the markdown file

        Returns:
            list: List of content blocks with headers and content
        """
        markdown_path = Path(markdown_file_path)

        if not markdown_path.exists():
            print(f"Warning: Markdown file {markdown_path} not found")
            return []

        try:
            with open(markdown_path, 'r', encoding='utf-8') as f:
                content = f.read()
        except (FileNotFoundError, PermissionError, UnicodeDecodeError) as e:
            print(f"Error reading {markdown_path}: {e}")
            return []

        return self._parse_markdown_content(content)

    def parse_excel_file(self, excel_file_path, sheet_name=None, table_title=None):
        """Parse an Excel file and extract table data.

        Args:
            excel_file_path (str or Path): Path to the Excel file
            sheet_name (str): Optional sheet name to read (default: first sheet)
            table_title (str): Optional title for the table

        Returns:
            dict: Dictionary with table data and metadata
        """
        excel_path = Path(excel_file_path)

        if not excel_path.exists():
            print(f"Warning: Excel file {excel_path} not found")
            return None

        try:
            # Read Excel file
            if sheet_name:
                data = pd.read_excel(excel_path, sheet_name=sheet_name)
            else:
                data = pd.read_excel(excel_path)

            # Get sheet info for title if not provided
            if not table_title:
                if sheet_name:
                    table_title = f"Table: {sheet_name}"
                else:
                    table_title = f"Table: {excel_path.stem}"

            return {
                'data': data,
                'title': table_title,
                'file_path': excel_path,
                'sheet_name': sheet_name,
                'columns': list(data.columns),
                'rows': len(data)
            }

        except (FileNotFoundError, PermissionError, ValueError, ImportError) as e:
            print(f"Error reading Excel file {excel_path}: {e}")
            return None

    def _parse_markdown_content(self, content):
        """Parse markdown content into structured blocks.

        Args:
            content (str): Raw markdown content

        Returns:
            list: List of dictionaries with 'type', 'level', 'text', and 'content'
        """
        blocks = []
        lines = content.split('\n')
        current_section = None
        current_content = []

        for line in lines:
            # Check for headers (# ## ### etc.)
            header_match = re.match(r'^(#{1,6})\s+(.+)', line.strip())

            if header_match:
                # Save previous section if exists
                if current_section is not None:
                    current_section['content'] = '\n'.join(
                        current_content).strip()
                    blocks.append(current_section)

                # Start new section
                level = len(header_match.group(1))  # Number of # characters
                title = header_match.group(2).strip()

                current_section = {
                    'type': 'header',
                    'level': level,
                    'text': title,
                    'content': ''
                }
                current_content = []
            else:
                # Add line to current content
                current_content.append(line)

        # Save last section
        if current_section:
            current_section['content'] = '\n'.join(current_content).strip()
            blocks.append(current_section)

        # If no headers found, treat entire content as one block
        if not blocks and content.strip():
            blocks.append({
                'type': 'content',
                'level': 0,
                'text': '',
                'content': content.strip()
            })

        return blocks

    def add_markdown_to_document(self, markdown_file_path, start_header_level=1):
        """Add markdown content to the Word document with proper formatting.

        Args:
            markdown_file_path (str or Path): Path to the markdown file
            start_header_level (int): Starting header level for Word document (1-9)

        Returns:
            bool: True if successful, False otherwise
        """
        blocks = self.parse_markdown_file(markdown_file_path)

        if not blocks:
            return False

        for block in blocks:
            if block['type'] == 'header':
                # Add header to document
                word_level = min(start_header_level + block['level'] - 1, 9)
                self.document.add_heading(block['text'], level=word_level)

                # Add content if any
                if block['content'].strip():
                    self._add_formatted_content(block['content'])
            else:
                # Add content without header
                if block['content'].strip():
                    self._add_formatted_content(block['content'])

        return True

    def add_excel_table_to_document(self, excel_file_path, sheet_name=None, table_title=None,
                                    include_header=True, start_header_level=1):
        """Add Excel table to the Word document with proper formatting.

        Args:
            excel_file_path (str or Path): Path to the Excel file
            sheet_name (str): Optional sheet name to read
            table_title (str): Optional title for the table
            include_header (bool): Whether to add a header before the table
            start_header_level (int): Header level for the table title

        Returns:
            bool: True if successful, False otherwise
        """
        excel_data = self.parse_excel_file(
            excel_file_path, sheet_name, table_title)

        if not excel_data:
            return False

        data = excel_data['data']
        title = excel_data['title']

        # Add header if requested
        if include_header:
            self.document.add_heading(title, level=start_header_level)

        # Add summary paragraph
        summary_text = f"Data summary: {excel_data['rows']} rows, {len(excel_data['columns'])} columns"
        self.document.add_paragraph(summary_text)

        # Create table
        table = self.document.add_table(rows=1, cols=len(data.columns))
        table.style = 'Table Grid'  # Use a clean table style

        # Add header row
        hdr_cells = table.rows[0].cells
        for i, column_name in enumerate(data.columns):
            hdr_cells[i].text = str(column_name)
            # Make header bold
            for paragraph in hdr_cells[i].paragraphs:
                for run in paragraph.runs:
                    run.bold = True

        # Add data rows
        for _, row_data in data.iterrows():
            row_cells = table.add_row().cells

            for j, cell_value in enumerate(row_data):
                # Format cell value
                if pd.api.types.is_numeric_dtype(type(cell_value)) and pd.notna(cell_value):
                    # Format numbers with appropriate precision
                    if isinstance(cell_value, float):
                        if cell_value == int(cell_value):
                            row_cells[j].text = f"{int(cell_value):,}"
                        else:
                            row_cells[j].text = f"{cell_value:,.2f}"
                    else:
                        row_cells[j].text = f"{cell_value:,}"
                else:
                    row_cells[j].text = str(
                        cell_value) if pd.notna(cell_value) else ""

                # Right-align numeric columns
                if pd.api.types.is_numeric_dtype(type(cell_value)):
                    for paragraph in row_cells[j].paragraphs:
                        paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT

        print(f"✅ Added Excel table: {title}")
        return True

    def add_mixed_content_to_document(self, content_files, start_header_level=1):
        """Add multiple markdown and Excel files to the document.

        Args:
            content_files (list): List of file paths or dictionaries with file info
            start_header_level (int): Starting header level

        Returns:
            dict: Results summary with success/failure counts
        """
        results = {'success': 0, 'failed': 0, 'files': []}

        for item in content_files:
            if isinstance(item, str):
                file_path = Path(item)
                options = {}
            else:
                file_path = Path(item['file'])
                options = item.get('options', {})

            try:
                if file_path.suffix.lower() in ['.md', '.markdown']:
                    # Handle markdown file
                    success = self.add_markdown_to_document(
                        file_path,
                        options.get('start_header_level', start_header_level)
                    )
                elif file_path.suffix.lower() in ['.xlsx', '.xls']:
                    # Handle Excel file
                    success = self.add_excel_table_to_document(
                        file_path,
                        sheet_name=options.get('sheet_name'),
                        table_title=options.get('table_title'),
                        include_header=options.get('include_header', True),
                        start_header_level=options.get(
                            'start_header_level', start_header_level)
                    )
                else:
                    print(f"⚠️  Unsupported file type: {file_path.suffix}")
                    success = False

                if success:
                    results['success'] += 1
                    results['files'].append(
                        {'file': str(file_path), 'status': 'success'})
                else:
                    results['failed'] += 1
                    results['files'].append(
                        {'file': str(file_path), 'status': 'failed'})

            except (FileNotFoundError, PermissionError, ValueError) as e:
                print(f"❌ Error processing {file_path}: {e}")
                results['failed'] += 1
                results['files'].append(
                    {'file': str(file_path), 'status': 'error', 'error': str(e)})

        return results

    def add_all_content_from_folder(self, start_header_level=1, include_patterns=None, exclude_patterns=None):
        """Automatically discover and add all content files from the content folder.

        Args:
            start_header_level (int): Starting header level
            include_patterns (list): List of glob patterns to include
            exclude_patterns (list): List of patterns to exclude

        Returns:
            dict: Results summary with success/failure counts
        """
        discovered = self.discover_content_files(
            include_patterns, exclude_patterns)

        if not discovered['all']:
            return {'success': 0, 'failed': 0, 'files': [], 'discovered': 0}

        # Convert Path objects to strings for add_mixed_content_to_document
        file_paths = [str(file_path) for file_path in discovered['all']]

        results = self.add_mixed_content_to_document(
            file_paths, start_header_level)
        results['discovered'] = len(discovered['all'])

        return results

    def _add_formatted_content(self, content):
        """Add formatted content to the document with markdown-style formatting.

        Args:
            content (str): Content to add with markdown formatting
        """
        # Split content into paragraphs
        paragraphs = content.split('\n\n')

        for para in paragraphs:
            para = para.strip()
            if not para:
                continue

            # Handle different types of content
            if para.startswith('- ') or para.startswith('* '):
                self._add_bullet_list(para)
            elif para.startswith('1. ') or re.match(r'^\d+\.\s', para):
                self._add_numbered_list(para)
            elif para.startswith('> '):
                self._add_blockquote(para)
            else:
                self._add_regular_paragraph(para)

    def _add_bullet_list(self, content):
        """Add bullet list items to the document."""
        lines = content.split('\n')
        for line in lines:
            line = line.strip()
            if line.startswith('- ') or line.startswith('* '):
                bullet_text = line[2:].strip()
                if bullet_text:
                    self.document.add_paragraph(
                        bullet_text, style='List Bullet')

    def _add_numbered_list(self, content):
        """Add numbered list items to the document."""
        lines = content.split('\n')
        for line in lines:
            line = line.strip()
            if re.match(r'^\d+\.\s', line):
                # Remove the number and period
                numbered_text = re.sub(r'^\d+\.\s', '', line)
                if numbered_text:
                    self.document.add_paragraph(
                        numbered_text, style='List Number')

    def _add_blockquote(self, content):
        """Add blockquote content to the document."""
        lines = content.split('\n')
        for line in lines:
            line = line.strip()
            if line.startswith('> '):
                quote_text = line[2:].strip()
                if quote_text:
                    para = self.document.add_paragraph(quote_text)
                    # Style as italic for blockquotes
                    for run in para.runs:
                        run.italic = True

    def _add_regular_paragraph(self, content):
        """Add regular paragraph with basic markdown formatting."""
        # Handle basic markdown formatting
        content = self._apply_basic_formatting(content)

        # Split by lines for multi-line paragraphs
        lines = content.split('\n')
        for line in lines:
            line = line.strip()
            if line:
                self.document.add_paragraph(line)

    def _apply_basic_formatting(self, text):
        """Apply basic markdown formatting (bold, italic) to text.

        Note: This is a simplified implementation. Word formatting
        would require more complex handling of runs.

        Args:
            text (str): Text with markdown formatting

        Returns:
            str: Text with formatting markers removed (for now)
        """
        # For now, just remove markdown formatting markers
        # In a more advanced implementation, we'd convert these to Word formatting
        text = re.sub(r'\*\*(.*?)\*\*', r'\1', text)  # Bold
        text = re.sub(r'\*(.*?)\*', r'\1', text)      # Italic
        text = re.sub(r'`(.*?)`', r'\1', text)        # Code

        return text

    def generate_auto_report(self, content_files, output_path, document_title=None):
        """Generate a complete Word document from multiple content files.

        Args:
            content_files (list): List of markdown and Excel files to include
            output_path (str or Path): Output path for the Word document
            document_title (str): Optional title for the document

        Returns:
            bool: True if successful, False otherwise
        """
        # Create new document
        self.document = Document()

        # Add document title if provided
        if document_title:
            self.document.add_heading(document_title, level=0)

        # Process all content files
        results = self.add_mixed_content_to_document(content_files)

        # Save document
        try:
            self.document.save(output_path)
            print(f"✅ Auto-generated report saved: {output_path}")
            print(
                f"📊 Processed {results['success']} files successfully, {results['failed']} failed")
            return True
        except (OSError, PermissionError) as e:
            print(f"❌ Error saving document: {e}")
            return False

__init__(self, document=None, content_folder='reports') special

Initialize the generator with an optional Word document.

Parameters:

Name Type Description Default
document

Existing Word document object, or None to create new one

None
content_folder str

Folder to scan for additional content files

'reports'
Source code in autorpt/gen_auto.py
def __init__(self, document=None, content_folder="reports"):
    """Initialize the generator with an optional Word document.

    Args:
        document: Existing Word document object, or None to create new one
        content_folder (str): Folder to scan for additional content files
    """
    self.document = document if document else Document()
    self.content_folder = Path(content_folder)

add_all_content_from_folder(self, start_header_level=1, include_patterns=None, exclude_patterns=None)

Automatically discover and add all content files from the content folder.

Parameters:

Name Type Description Default
start_header_level int

Starting header level

1
include_patterns list

List of glob patterns to include

None
exclude_patterns list

List of patterns to exclude

None

Returns:

Type Description
dict

Results summary with success/failure counts

Source code in autorpt/gen_auto.py
def add_all_content_from_folder(self, start_header_level=1, include_patterns=None, exclude_patterns=None):
    """Automatically discover and add all content files from the content folder.

    Args:
        start_header_level (int): Starting header level
        include_patterns (list): List of glob patterns to include
        exclude_patterns (list): List of patterns to exclude

    Returns:
        dict: Results summary with success/failure counts
    """
    discovered = self.discover_content_files(
        include_patterns, exclude_patterns)

    if not discovered['all']:
        return {'success': 0, 'failed': 0, 'files': [], 'discovered': 0}

    # Convert Path objects to strings for add_mixed_content_to_document
    file_paths = [str(file_path) for file_path in discovered['all']]

    results = self.add_mixed_content_to_document(
        file_paths, start_header_level)
    results['discovered'] = len(discovered['all'])

    return results

add_excel_table_to_document(self, excel_file_path, sheet_name=None, table_title=None, include_header=True, start_header_level=1)

Add Excel table to the Word document with proper formatting.

Parameters:

Name Type Description Default
excel_file_path str or Path

Path to the Excel file

required
sheet_name str

Optional sheet name to read

None
table_title str

Optional title for the table

None
include_header bool

Whether to add a header before the table

True
start_header_level int

Header level for the table title

1

Returns:

Type Description
bool

True if successful, False otherwise

Source code in autorpt/gen_auto.py
def add_excel_table_to_document(self, excel_file_path, sheet_name=None, table_title=None,
                                include_header=True, start_header_level=1):
    """Add Excel table to the Word document with proper formatting.

    Args:
        excel_file_path (str or Path): Path to the Excel file
        sheet_name (str): Optional sheet name to read
        table_title (str): Optional title for the table
        include_header (bool): Whether to add a header before the table
        start_header_level (int): Header level for the table title

    Returns:
        bool: True if successful, False otherwise
    """
    excel_data = self.parse_excel_file(
        excel_file_path, sheet_name, table_title)

    if not excel_data:
        return False

    data = excel_data['data']
    title = excel_data['title']

    # Add header if requested
    if include_header:
        self.document.add_heading(title, level=start_header_level)

    # Add summary paragraph
    summary_text = f"Data summary: {excel_data['rows']} rows, {len(excel_data['columns'])} columns"
    self.document.add_paragraph(summary_text)

    # Create table
    table = self.document.add_table(rows=1, cols=len(data.columns))
    table.style = 'Table Grid'  # Use a clean table style

    # Add header row
    hdr_cells = table.rows[0].cells
    for i, column_name in enumerate(data.columns):
        hdr_cells[i].text = str(column_name)
        # Make header bold
        for paragraph in hdr_cells[i].paragraphs:
            for run in paragraph.runs:
                run.bold = True

    # Add data rows
    for _, row_data in data.iterrows():
        row_cells = table.add_row().cells

        for j, cell_value in enumerate(row_data):
            # Format cell value
            if pd.api.types.is_numeric_dtype(type(cell_value)) and pd.notna(cell_value):
                # Format numbers with appropriate precision
                if isinstance(cell_value, float):
                    if cell_value == int(cell_value):
                        row_cells[j].text = f"{int(cell_value):,}"
                    else:
                        row_cells[j].text = f"{cell_value:,.2f}"
                else:
                    row_cells[j].text = f"{cell_value:,}"
            else:
                row_cells[j].text = str(
                    cell_value) if pd.notna(cell_value) else ""

            # Right-align numeric columns
            if pd.api.types.is_numeric_dtype(type(cell_value)):
                for paragraph in row_cells[j].paragraphs:
                    paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT

    print(f"✅ Added Excel table: {title}")
    return True

add_markdown_to_document(self, markdown_file_path, start_header_level=1)

Add markdown content to the Word document with proper formatting.

Parameters:

Name Type Description Default
markdown_file_path str or Path

Path to the markdown file

required
start_header_level int

Starting header level for Word document (1-9)

1

Returns:

Type Description
bool

True if successful, False otherwise

Source code in autorpt/gen_auto.py
def add_markdown_to_document(self, markdown_file_path, start_header_level=1):
    """Add markdown content to the Word document with proper formatting.

    Args:
        markdown_file_path (str or Path): Path to the markdown file
        start_header_level (int): Starting header level for Word document (1-9)

    Returns:
        bool: True if successful, False otherwise
    """
    blocks = self.parse_markdown_file(markdown_file_path)

    if not blocks:
        return False

    for block in blocks:
        if block['type'] == 'header':
            # Add header to document
            word_level = min(start_header_level + block['level'] - 1, 9)
            self.document.add_heading(block['text'], level=word_level)

            # Add content if any
            if block['content'].strip():
                self._add_formatted_content(block['content'])
        else:
            # Add content without header
            if block['content'].strip():
                self._add_formatted_content(block['content'])

    return True

add_mixed_content_to_document(self, content_files, start_header_level=1)

Add multiple markdown and Excel files to the document.

Parameters:

Name Type Description Default
content_files list

List of file paths or dictionaries with file info

required
start_header_level int

Starting header level

1

Returns:

Type Description
dict

Results summary with success/failure counts

Source code in autorpt/gen_auto.py
def add_mixed_content_to_document(self, content_files, start_header_level=1):
    """Add multiple markdown and Excel files to the document.

    Args:
        content_files (list): List of file paths or dictionaries with file info
        start_header_level (int): Starting header level

    Returns:
        dict: Results summary with success/failure counts
    """
    results = {'success': 0, 'failed': 0, 'files': []}

    for item in content_files:
        if isinstance(item, str):
            file_path = Path(item)
            options = {}
        else:
            file_path = Path(item['file'])
            options = item.get('options', {})

        try:
            if file_path.suffix.lower() in ['.md', '.markdown']:
                # Handle markdown file
                success = self.add_markdown_to_document(
                    file_path,
                    options.get('start_header_level', start_header_level)
                )
            elif file_path.suffix.lower() in ['.xlsx', '.xls']:
                # Handle Excel file
                success = self.add_excel_table_to_document(
                    file_path,
                    sheet_name=options.get('sheet_name'),
                    table_title=options.get('table_title'),
                    include_header=options.get('include_header', True),
                    start_header_level=options.get(
                        'start_header_level', start_header_level)
                )
            else:
                print(f"⚠️  Unsupported file type: {file_path.suffix}")
                success = False

            if success:
                results['success'] += 1
                results['files'].append(
                    {'file': str(file_path), 'status': 'success'})
            else:
                results['failed'] += 1
                results['files'].append(
                    {'file': str(file_path), 'status': 'failed'})

        except (FileNotFoundError, PermissionError, ValueError) as e:
            print(f"❌ Error processing {file_path}: {e}")
            results['failed'] += 1
            results['files'].append(
                {'file': str(file_path), 'status': 'error', 'error': str(e)})

    return results

discover_content_files(self, include_patterns=None, exclude_patterns=None)

Automatically discover markdown and Excel files in the content folder.

Parameters:

Name Type Description Default
include_patterns list

List of glob patterns to include (default: ['.md', '.xlsx', '*.xls'])

None
exclude_patterns list

List of patterns to exclude (default: ['report.docx'])

None

Returns:

Type Description
dict

Dictionary with 'markdown' and 'excel' file lists

Source code in autorpt/gen_auto.py
def discover_content_files(self, include_patterns=None, exclude_patterns=None):
    """Automatically discover markdown and Excel files in the content folder.

    Args:
        include_patterns (list): List of glob patterns to include (default: ['*.md', '*.xlsx', '*.xls'])
        exclude_patterns (list): List of patterns to exclude (default: ['*report*.docx'])

    Returns:
        dict: Dictionary with 'markdown' and 'excel' file lists
    """
    if include_patterns is None:
        include_patterns = ['*.md', '*.markdown', '*.xlsx', '*.xls']

    if exclude_patterns is None:
        exclude_patterns = ['*report*.docx', '*report*.pdf']

    discovered = {'markdown': [], 'excel': [], 'all': []}

    if not self.content_folder.exists():
        print(f"📁 Content folder '{self.content_folder}' not found")
        return discovered

    try:
        # Find all matching files
        all_files = []
        for pattern in include_patterns:
            all_files.extend(self.content_folder.glob(pattern))

        # Filter out excluded patterns
        filtered_files = []
        for file_path in all_files:
            exclude_file = False
            for exclude_pattern in exclude_patterns:
                if file_path.match(exclude_pattern):
                    exclude_file = True
                    break
            if not exclude_file:
                filtered_files.append(file_path)

        # Categorize files
        for file_path in filtered_files:
            if file_path.suffix.lower() in ['.md', '.markdown']:
                discovered['markdown'].append(file_path)
            elif file_path.suffix.lower() in ['.xlsx', '.xls']:
                discovered['excel'].append(file_path)
            discovered['all'].append(file_path)

        # Sort files by name for consistent ordering
        discovered['markdown'].sort(key=lambda x: x.name)
        discovered['excel'].sort(key=lambda x: x.name)
        discovered['all'].sort(key=lambda x: x.name)

        if discovered['all']:
            print(
                f"📂 Discovered {len(discovered['all'])} content files in '{self.content_folder}':")
            for file_path in discovered['all']:
                print(f"   📄 {file_path.name}")
        else:
            print(
                f"📂 No additional content files found in '{self.content_folder}'")

    except (OSError, PermissionError) as e:
        print(f"❌ Error discovering content files: {e}")

    return discovered

generate_auto_report(self, content_files, output_path, document_title=None)

Generate a complete Word document from multiple content files.

Parameters:

Name Type Description Default
content_files list

List of markdown and Excel files to include

required
output_path str or Path

Output path for the Word document

required
document_title str

Optional title for the document

None

Returns:

Type Description
bool

True if successful, False otherwise

Source code in autorpt/gen_auto.py
def generate_auto_report(self, content_files, output_path, document_title=None):
    """Generate a complete Word document from multiple content files.

    Args:
        content_files (list): List of markdown and Excel files to include
        output_path (str or Path): Output path for the Word document
        document_title (str): Optional title for the document

    Returns:
        bool: True if successful, False otherwise
    """
    # Create new document
    self.document = Document()

    # Add document title if provided
    if document_title:
        self.document.add_heading(document_title, level=0)

    # Process all content files
    results = self.add_mixed_content_to_document(content_files)

    # Save document
    try:
        self.document.save(output_path)
        print(f"✅ Auto-generated report saved: {output_path}")
        print(
            f"📊 Processed {results['success']} files successfully, {results['failed']} failed")
        return True
    except (OSError, PermissionError) as e:
        print(f"❌ Error saving document: {e}")
        return False

parse_excel_file(self, excel_file_path, sheet_name=None, table_title=None)

Parse an Excel file and extract table data.

Parameters:

Name Type Description Default
excel_file_path str or Path

Path to the Excel file

required
sheet_name str

Optional sheet name to read (default: first sheet)

None
table_title str

Optional title for the table

None

Returns:

Type Description
dict

Dictionary with table data and metadata

Source code in autorpt/gen_auto.py
def parse_excel_file(self, excel_file_path, sheet_name=None, table_title=None):
    """Parse an Excel file and extract table data.

    Args:
        excel_file_path (str or Path): Path to the Excel file
        sheet_name (str): Optional sheet name to read (default: first sheet)
        table_title (str): Optional title for the table

    Returns:
        dict: Dictionary with table data and metadata
    """
    excel_path = Path(excel_file_path)

    if not excel_path.exists():
        print(f"Warning: Excel file {excel_path} not found")
        return None

    try:
        # Read Excel file
        if sheet_name:
            data = pd.read_excel(excel_path, sheet_name=sheet_name)
        else:
            data = pd.read_excel(excel_path)

        # Get sheet info for title if not provided
        if not table_title:
            if sheet_name:
                table_title = f"Table: {sheet_name}"
            else:
                table_title = f"Table: {excel_path.stem}"

        return {
            'data': data,
            'title': table_title,
            'file_path': excel_path,
            'sheet_name': sheet_name,
            'columns': list(data.columns),
            'rows': len(data)
        }

    except (FileNotFoundError, PermissionError, ValueError, ImportError) as e:
        print(f"Error reading Excel file {excel_path}: {e}")
        return None

parse_markdown_file(self, markdown_file_path)

Parse a markdown file and extract structured content.

Parameters:

Name Type Description Default
markdown_file_path str or Path

Path to the markdown file

required

Returns:

Type Description
list

List of content blocks with headers and content

Source code in autorpt/gen_auto.py
def parse_markdown_file(self, markdown_file_path):
    """Parse a markdown file and extract structured content.

    Args:
        markdown_file_path (str or Path): Path to the markdown file

    Returns:
        list: List of content blocks with headers and content
    """
    markdown_path = Path(markdown_file_path)

    if not markdown_path.exists():
        print(f"Warning: Markdown file {markdown_path} not found")
        return []

    try:
        with open(markdown_path, 'r', encoding='utf-8') as f:
            content = f.read()
    except (FileNotFoundError, PermissionError, UnicodeDecodeError) as e:
        print(f"Error reading {markdown_path}: {e}")
        return []

    return self._parse_markdown_content(content)

add_excel_table_to_existing_document(document, excel_file, sheet_name=None, table_title=None, start_level=1)

Add Excel table to an existing Word document.

Parameters:

Name Type Description Default
document

Word document object

required
excel_file str

Path to Excel file

required
sheet_name str

Optional sheet name

None
table_title str

Optional table title

None
start_level int

Starting header level (1-9)

1

Returns:

Type Description
bool

True if successful, False otherwise

Source code in autorpt/gen_auto.py
def add_excel_table_to_existing_document(document, excel_file, sheet_name=None,
                                         table_title=None, start_level=1):
    """Add Excel table to an existing Word document.

    Args:
        document: Word document object
        excel_file (str): Path to Excel file
        sheet_name (str): Optional sheet name
        table_title (str): Optional table title
        start_level (int): Starting header level (1-9)

    Returns:
        bool: True if successful, False otherwise
    """
    generator = AutoReportGenerator(document)
    return generator.add_excel_table_to_document(
        excel_file, sheet_name, table_title, True, start_level
    )

add_markdown_to_existing_document(document, markdown_file, start_level=1)

Add markdown content to an existing Word document.

Parameters:

Name Type Description Default
document

Word document object

required
markdown_file str

Path to markdown file

required
start_level int

Starting header level (1-9)

1

Returns:

Type Description
bool

True if successful, False otherwise

Source code in autorpt/gen_auto.py
def add_markdown_to_existing_document(document, markdown_file, start_level=1):
    """Add markdown content to an existing Word document.

    Args:
        document: Word document object
        markdown_file (str): Path to markdown file
        start_level (int): Starting header level (1-9)

    Returns:
        bool: True if successful, False otherwise
    """
    generator = AutoReportGenerator(document)
    return generator.add_markdown_to_document(markdown_file, start_level)

add_mixed_content_to_existing_document(document, content_files, start_level=1)

Add markdown and Excel content to an existing Word document.

Parameters:

Name Type Description Default
document

Word document object

required
content_files list

List of content files to add

required
start_level int

Starting header level (1-9)

1

Returns:

Type Description
dict

Results summary

Source code in autorpt/gen_auto.py
def add_mixed_content_to_existing_document(document, content_files, start_level=1):
    """Add markdown and Excel content to an existing Word document.

    Args:
        document: Word document object
        content_files (list): List of content files to add
        start_level (int): Starting header level (1-9)

    Returns:
        dict: Results summary
    """
    generator = AutoReportGenerator(document)
    return generator.add_mixed_content_to_document(content_files, start_level)

auto_generate_from_folder(content_folder='reports', output_file=None, document_title=None, include_patterns=None, exclude_patterns=None)

Automatically discover and convert all content files in a folder to a Word document.

Parameters:

Name Type Description Default
content_folder str

Folder to scan for content files (default: "reports")

'reports'
output_file str

Output path for Word document (auto-generated if None)

None
document_title str

Optional document title

None
include_patterns list

File patterns to include

None
exclude_patterns list

File patterns to exclude

None

Returns:

Type Description
dict

Results with success/failure counts and output file path

Source code in autorpt/gen_auto.py
def auto_generate_from_folder(content_folder="reports", output_file=None, document_title=None,
                              include_patterns=None, exclude_patterns=None):
    """Automatically discover and convert all content files in a folder to a Word document.

    Args:
        content_folder (str): Folder to scan for content files (default: "reports")
        output_file (str): Output path for Word document (auto-generated if None)
        document_title (str): Optional document title
        include_patterns (list): File patterns to include
        exclude_patterns (list): File patterns to exclude

    Returns:
        dict: Results with success/failure counts and output file path
    """
    generator = AutoReportGenerator(content_folder=content_folder)

    # Generate output filename if not provided
    if not output_file:
        from datetime import datetime
        content_path = Path(content_folder)
        timestamp = datetime.now().strftime("%Y-%m-%d_%H%M")
        output_file = content_path / f"auto_report_{timestamp}.docx"

    # Create new document with title
    generator.document = Document()
    if document_title:
        generator.document.add_heading(document_title, level=0)
    elif not document_title:
        generator.document.add_heading("Auto-Generated Report", level=0)
        # Add generation info
        from datetime import datetime
        date_para = generator.document.add_paragraph(
            f"Generated on: {datetime.now().strftime('%B %d, %Y at %I:%M %p')}"
        )
        date_para.alignment = WD_ALIGN_PARAGRAPH.CENTER

    # Process all discovered files
    results = generator.add_all_content_from_folder(
        start_header_level=1,
        include_patterns=include_patterns,
        exclude_patterns=exclude_patterns
    )

    # Save document
    try:
        generator.document.save(output_file)
        results['output_file'] = str(output_file)
        results['saved'] = True
        print(f"✅ Auto-generated report saved: {output_file}")
        print(
            f"📊 Processed {results['success']}/{results['discovered']} files successfully")
        if results['failed'] > 0:
            print(f"⚠️  {results['failed']} files failed to process")
    except (OSError, PermissionError) as e:
        results['saved'] = False
        results['error'] = str(e)
        print(f"❌ Error saving document: {e}")

    return results

convert_mixed_content_to_word(content_files, output_file, document_title=None)

Convert multiple markdown and Excel files to a Word document.

Parameters:

Name Type Description Default
content_files list

List of file paths or file dictionaries

required
output_file str

Output path for Word document

required
document_title str

Optional document title

None

Returns:

Type Description
bool

True if successful, False otherwise

Source code in autorpt/gen_auto.py
def convert_mixed_content_to_word(content_files, output_file, document_title=None):
    """Convert multiple markdown and Excel files to a Word document.

    Args:
        content_files (list): List of file paths or file dictionaries
        output_file (str): Output path for Word document
        document_title (str): Optional document title

    Returns:
        bool: True if successful, False otherwise
    """
    generator = AutoReportGenerator()
    return generator.generate_auto_report(content_files, output_file, document_title)