File size: 9,574 Bytes
402122a
 
 
 
 
 
 
 
 
 
 
 
 
d5e836f
402122a
 
 
 
 
 
de0b6bd
402122a
 
3c0d939
57e5629
 
402122a
 
 
57e5629
d5e836f
402122a
57e5629
 
 
402122a
 
 
 
 
 
 
 
 
 
 
 
d734bf7
 
 
 
402122a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88a3921
 
7b50d28
 
 
88a3921
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402122a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d734bf7
 
 
 
 
402122a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09421ea
 
402122a
09421ea
 
402122a
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# Word Generator Module
# Contains create_word_document() and all Word-related functions

import re
import os
import tempfile
import time
import io
import sys
from typing import List, Dict, Any

# Add the parent directory to sys.path to import from models.py
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from models import ExportRequest

# Import from the same directory (Document Generation)
from document_helpers import (
    filter_empty_sections,
    extract_business_name_from_content
)
# from table_of_content import generate_table_of_contents  # Not used in word generator
from chart_generator import parse_embedded_chart_data, create_chart_from_data
from markdown_renderer import (
    render_markdown_to_docx_grouped,
    process_content_with_inline_charts,
    clean_text_for_pdf
)

# Add Currency Mapping path
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'CurrencyMapping'))
from CurrencyMapping.currency_mapping import convert_currency_symbols_to_iso

# Import sort_sections_by_order from utils module
from utils import sort_sections_by_order

def create_word_document(request: ExportRequest):
    """Create a Word document from the business plan data with front page, TOC, and proper page breaks"""
    try:
        from docx import Document
        from docx.shared import Inches, Pt, RGBColor
        from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_TAB_ALIGNMENT
        from docx.oxml import OxmlElement
        from docx.oxml.ns import qn
    except ImportError:
        raise Exception("python-docx is not installed. Please install it with: pip install python-docx")
    
    # Filter out empty sections to prevent blank pages
    try:
        filtered_sections = filter_empty_sections(request.sections)
    except Exception as e:
        raise Exception(f"Failed to filter sections: {str(e)}")
    if not filtered_sections:
        raise Exception("No valid sections found after filtering. All sections appear to be empty.")
    
    doc = Document()
    
    # Add confidentiality footer to every page
    section = doc.sections[0]
    footer = section.footer
    footer_para = footer.paragraphs[0]
    footer_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
    footer_run = footer_para.add_run("CONFIDENTIAL - This document contains proprietary information and may not be shared without consent")
    footer_run.font.size = Pt(8)
    footer_run.font.color.rgb = RGBColor(100, 100, 100)  # Gray text
    
    # ===== FRONT PAGE =====
    # Add company logo at the top
    try:
        # Use the MBD logo PNG file - now in app folder for HF deployment
        app_root = os.path.dirname(os.path.dirname(__file__))
        logo_path = os.path.join(app_root, 'assets', 'logos', 'MBD logo.png')
        
        if os.path.exists(logo_path):
            # Add logo to Word document (centered, top of page)
            logo_para = doc.add_paragraph()
            logo_para.alignment = WD_ALIGN_PARAGRAPH.CENTER
            logo_run = logo_para.add_run()
            logo_run.add_picture(logo_path, width=Inches(3.0))  # Even larger size to show full logo
            print(f"Logo added successfully from: {logo_path}")
        else:
            print(f"Logo file not found at: {logo_path}")
    except Exception as e:
        # Log any errors with logo
        print(f"Logo error: {e}")
        pass
    
    # Add top spacing
    doc.add_paragraph()
    doc.add_paragraph()
    
    # Extract business name from content, fallback to businessIdea
    extracted_business_name = extract_business_name_from_content(filtered_sections)
    business_name = extracted_business_name or request.businessIdea or "Business Plan"
    
    # Business name as main title with enhanced styling - PURPLE THEME
    title = doc.add_heading(business_name, 0)
    title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    # Style the title with larger font and PURPLE color
    for run in title.runs:
        run.font.size = Pt(36)
        run.font.color.rgb = RGBColor(139, 92, 246)  # PURPLE
    
    # Removed decorative line under title
    
    # Add subtitle with enhanced styling - PURPLE
    doc.add_paragraph()
    subtitle = doc.add_paragraph("Professional Business Plan Document")
    subtitle.alignment = WD_ALIGN_PARAGRAPH.CENTER
    subtitle_run = subtitle.runs[0]
    subtitle_run.font.size = Pt(18)
    subtitle_run.font.color.rgb = RGBColor(124, 58, 237)  # DARKER PURPLE
    subtitle_run.italic = True
    
    # Add more spacing
    doc.add_paragraph()
    doc.add_paragraph()
    doc.add_paragraph()
    
    # Add document info in a styled box - PURPLE BORDER
    info_para = doc.add_paragraph()
    info_para.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    # Add background color and border styling
    info_run = info_para.add_run("Prepared by: Business Plan Generator")
    info_run.font.size = Pt(14)
    info_run.font.color.rgb = RGBColor(139, 92, 246)  # PURPLE
    
    doc.add_paragraph()
    date_para = doc.add_paragraph()
    date_para.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER
    date_run = date_para.add_run(f"Date: {time.strftime('%B %Y')}")
    date_run.font.size = Pt(14)
    date_run.font.color.rgb = RGBColor(139, 92, 246)  # PURPLE
    
    # Confidentiality statement removed - now in footer of every page
    
    # Add more spacing before footer
    doc.add_paragraph()
    doc.add_paragraph()
    doc.add_paragraph()
    doc.add_paragraph()
    
    # Add footer with enhanced styling - PURPLE
    footer = doc.add_paragraph("Confidential Business Information")
    footer.alignment = WD_ALIGN_PARAGRAPH.CENTER
    footer_run = footer.runs[0]
    footer_run.font.size = Pt(12)
    footer_run.font.color.rgb = RGBColor(139, 92, 246)  # PURPLE
    footer_run.bold = True
    
    # Removed decorative line above footer
    
    # ===== TABLE OF CONTENTS =====
    # Add page break before TOC
    doc.add_page_break()
    
    # Add TOC title
    toc_title = doc.add_heading("Table of Contents", level=1)
    toc_title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    # Removed decorative line under TOC title
    
    # Add spacing before TOC entries
    doc.add_paragraph()
    
    # Sort sections according to predefined order (do this before TOC generation)
    try:
        sorted_sections = sort_sections_by_order(filtered_sections)
    except Exception as e:
        # Fallback: use filtered order if sorting fails
        sorted_sections = filtered_sections
    
    # Generate TOC entries with actual page numbers
    # Since each section starts on a new page, we can calculate page numbers
    toc_items = []
    current_page = 3  # Start after cover (1) and TOC (2)
    
    for section in sorted_sections:
        if section.content and section.content.strip():
            # Add main section
            toc_items.append({
                "title": section.title,
                "level": 1,
                "page": current_page
            })
            
            # Skip subheadings and chart titles - only include main section headings
            
            current_page += 1  # Each section starts on a new page
    
    # Add the actual TOC entries
    for item in toc_items:
        # Create TOC entry paragraph
        toc_para = doc.add_paragraph()
        toc_para.paragraph_format.space_after = Pt(6)
        
        # Add section title
        title_run = toc_para.add_run(item["title"])
        title_run.font.size = Pt(11)
        
        # Add dots and page number
        if item["level"] == 1:
            # Main section - bold
            title_run.bold = True
            # Add dots
            dots_run = toc_para.add_run(" " + "." * (60 - len(item["title"])) + " ")
            dots_run.font.size = Pt(11)
            dots_run.font.color.rgb = RGBColor(128, 128, 128)  # Gray
            # Add page number
            page_run = toc_para.add_run(str(item["page"]))
            page_run.font.size = Pt(11)
            page_run.bold = True
        else:
            # Subsection - indented
            toc_para.paragraph_format.left_indent = Inches(0.3)
            # Add dots
            dots_run = toc_para.add_run(" " + "." * (50 - len(item["title"])) + " ")
            dots_run.font.size = Pt(11)
            dots_run.font.color.rgb = RGBColor(128, 128, 128)  # Gray
            # Add page number
            page_run = toc_para.add_run(str(item["page"]))
            page_run.font.size = Pt(11)
    
    # logging.info(f"Generated {len(toc_items)} TOC items with actual page numbers")
    
    # Executive Summary is now handled as a regular section above
    
    # ===== MAIN SECTIONS =====
    for i, section in enumerate(sorted_sections):
        if section.content.strip():
            # Add page break before each section (including Executive Summary)
            doc.add_page_break()
            
            # Don't add section heading here - it's already in the TOC
            # The section title is already displayed in the Table of Contents
            
            # Process content with inline charts - use ORIGINAL content to preserve chart markers
            process_content_with_inline_charts(doc, section.content, request.businessIdea or "Business")
            
            doc.add_paragraph()
    
    # Charts are now handled inline with the sections above
    
    # Save to bytes
    buffer = io.BytesIO()
    doc.save(buffer)
    buffer.seek(0)
    doc_bytes = buffer.getvalue()
    buffer.close()
    
    filename = f"business_plan_{request.businessIdea or 'export'}.docx"
    return doc_bytes, filename