Zenaight commited on
Commit
fd25f77
·
verified ·
1 Parent(s): a508be1

Delete app/DocumentGeneration/grouped.py

Browse files
Files changed (1) hide show
  1. app/DocumentGeneration/grouped.py +0 -304
app/DocumentGeneration/grouped.py DELETED
@@ -1,304 +0,0 @@
1
- import re
2
- from typing import List, Dict, Any
3
-
4
- def calculate_group_height(pdf, group: Dict[str, Any]) -> int:
5
- """Calculate the estimated height needed for a complete group"""
6
- subheading_type = group['subheading_type']
7
- content_lines = group['content'].split('\n') if group['content'] else []
8
-
9
- # Base height for subheading
10
- if subheading_type == 'h1':
11
- subheading_height = 25 # 20pt font + spacing
12
- elif subheading_type == 'h2':
13
- subheading_height = 20 # 16pt font + underline + spacing
14
- elif subheading_type == 'h3':
15
- subheading_height = 16 # 14pt font + spacing
16
- elif subheading_type == 'h4':
17
- subheading_height = 14 # 12pt font + spacing
18
- elif subheading_type == 'bold':
19
- subheading_height = 20 # 16pt font + spacing
20
- elif subheading_type == 'numbered':
21
- subheading_height = 20 # 16pt font + spacing
22
- else:
23
- subheading_height = 16
24
-
25
- # Calculate content height more accurately
26
- content_height = 0
27
- for line in content_lines:
28
- if line.strip():
29
- # More accurate line height calculation
30
- words = line.split()
31
- if len(words) > 0:
32
- # Estimate characters per line (roughly 80-90 chars per line)
33
- chars_per_line = 85
34
- estimated_lines = max(1, len(line) // chars_per_line + 1)
35
- line_height = estimated_lines * 8 # 8pt per line
36
- content_height += line_height
37
- else:
38
- content_height += 8 # Empty line
39
- else:
40
- content_height += 3 # Empty line spacing
41
-
42
- # Add spacing between groups
43
- group_spacing = 8
44
-
45
- total_height = subheading_height + content_height + group_spacing
46
-
47
- print(f"DEBUG: Group '{group.get('subheading_clean', 'Unknown')[:30]}...' height calculation:")
48
- print(f" Subheading height: {subheading_height}")
49
- print(f" Content height: {content_height}")
50
- print(f" Group spacing: {group_spacing}")
51
- print(f" Total height: {total_height}")
52
-
53
- return total_height
54
-
55
- def should_break_page_for_group(pdf, group: Dict[str, Any], current_y: int, page_height: int = 280) -> bool:
56
- """Determine if we should break to a new page for this group"""
57
- estimated_height = calculate_group_height(pdf, group)
58
-
59
- print(f"DEBUG: should_break_page_for_group called")
60
- print(f"DEBUG: Subheading: '{group.get('subheading_clean', 'Unknown')[:30]}...'")
61
- print(f"DEBUG: Current Y: {current_y}")
62
- print(f"DEBUG: Estimated height: {estimated_height}")
63
- print(f"DEBUG: Page height: {page_height}")
64
- print(f"DEBUG: Available space: {page_height - current_y}")
65
- print(f"DEBUG: Will fit? {current_y + estimated_height <= page_height}")
66
-
67
- # CRITICAL FIX: Only break if the group absolutely won't fit
68
- # This prevents splitting subheadings from their content
69
-
70
- # If the group won't fit on current page, break
71
- if current_y + estimated_height > page_height:
72
- print(f"DEBUG: -> BREAKING: Group won't fit ({current_y} + {estimated_height} > {page_height})")
73
- return True
74
-
75
- # If we're very close to the end of the page (less than 50mm remaining) and group is significant, break
76
- if current_y > page_height - 50 and estimated_height > 30:
77
- print(f"DEBUG: -> BREAKING: Very close to end of page ({current_y} > {page_height - 50}) and group significant ({estimated_height} > 30)")
78
- return True
79
-
80
- print(f"DEBUG: -> NO BREAK: Group will fit on current page")
81
- return False
82
-
83
- def clean_markdown_from_line(line: str) -> str:
84
- """Clean markdown formatting from a single line"""
85
- if not line:
86
- return ""
87
-
88
- cleaned = line
89
-
90
- # Remove bold, italic formatting
91
- cleaned = re.sub(r'\*\*(.*?)\*\*', r'\1', cleaned) # Remove bold
92
- cleaned = re.sub(r'\*(.*?)\*', r'\1', cleaned) # Remove italic
93
- cleaned = re.sub(r'`(.*?)`', r'\1', cleaned) # Remove code
94
- cleaned = re.sub(r'~~(.*?)~~', r'\1', cleaned) # Remove strikethrough
95
-
96
- # Remove emphasis markers
97
- cleaned = re.sub(r'_{1,2}(.*?)_{1,2}', r'\1', cleaned) # Remove underscores
98
- cleaned = re.sub(r'\*{1,2}(.*?)\*{1,2}', r'\1', cleaned) # Remove asterisks
99
-
100
- # Remove links (keep text)
101
- cleaned = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', cleaned) # Remove links, keep text
102
-
103
- # Remove any remaining HTML-like tags
104
- cleaned = re.sub(r'<[^>]+>', '', cleaned)
105
-
106
- # CRITICAL FIX: Replace multiple spaces with single space to prevent double spaces
107
- cleaned = re.sub(r' +', ' ', cleaned) # Replace multiple spaces with single space
108
-
109
- return cleaned.strip()
110
-
111
- def parse_content_into_groups(content: str) -> List[Dict[str, Any]]:
112
- """Parse markdown content into subheading-content groups"""
113
- if not content:
114
- return []
115
-
116
- print(f"DEBUG: parse_content_into_groups called with content length: {len(content)}")
117
-
118
- lines = content.split('\n')
119
- groups = []
120
- current_group = None
121
-
122
- for i, line in enumerate(lines):
123
- line = line.strip()
124
- if not line:
125
- if current_group:
126
- current_group['content_lines'].append('') # Preserve empty lines
127
- continue
128
-
129
- # Check if this is a heading (H1, H2, H3, H4, or numbered)
130
- is_heading = (
131
- line.startswith('# ') or # H1
132
- (line.startswith('## ') and not line.startswith('### ')) or # H2
133
- (line.startswith('### ') and not line.startswith('#### ')) or # H3
134
- (line.startswith('#### ') and not line.startswith('##### ')) or # H4
135
- re.match(r'^\d+\.\d+\s+', line) # Numbered subheadings like "3.6 SWOT Analysis"
136
- )
137
-
138
- # Check if this is bold text
139
- is_bold_text = (
140
- (line.startswith('**') and line.endswith('**') and len(line) > 4) or # Bold headings
141
- (re.match(r'^\*\*.*?\*\*:', line) and len(line) > 4) # Bold text before colon as heading
142
- )
143
-
144
- # Check if this is a bold list item (e.g., "- **Item**")
145
- is_bold_list_item = re.match(r'^[-*]\s*\*\*(.*?)\*\*$', line)
146
-
147
- # Special handling for bold text following numbered subheadings
148
- should_create_new_group = False
149
-
150
- if is_heading:
151
- # Always create new group for regular headings
152
- should_create_new_group = True
153
- print(f"DEBUG: Line {i+1} '{line[:50]}...' - Regular heading, creating new group")
154
- elif is_bold_text:
155
- # Check if we should treat this bold text as content for a numbered subheading
156
- if groups and groups[-1].get('subheading_type') == 'h3':
157
- # Check if the previous group's subheading looks like a numbered subheading
158
- previous_subheading = groups[-1].get('subheading', '')
159
- if re.match(r'^\d+\.\d+\s+', previous_subheading):
160
- # This is content for a numbered subheading, add to previous group
161
- groups[-1]['content_lines'].append(line)
162
- print(f"DEBUG: Line {i+1} '{line[:50]}...' - Adding to previous numbered subheading '{previous_subheading[:30]}...'")
163
- continue # Skip creating new group
164
- else:
165
- # Not a numbered subheading, treat as new heading
166
- should_create_new_group = True
167
- print(f"DEBUG: Line {i+1} '{line[:50]}...' - Treating as NEW HEADING (not numbered subheading)")
168
- else:
169
- # No previous group or not a numbered subheading, treat as new heading
170
- should_create_new_group = True
171
- print(f"DEBUG: Line {i+1} '{line[:50]}...' - Treating as NEW HEADING (no numbered subheading)")
172
- elif is_bold_list_item:
173
- # If it's a bold list item, treat it as a new heading
174
- should_create_new_group = True
175
- print(f"DEBUG: Line {i+1} '{line[:50]}...' - Bold list item, creating new group")
176
-
177
- if should_create_new_group:
178
- # Save previous group if it exists
179
- if current_group:
180
- current_group['content'] = '\n'.join(current_group['content_lines'])
181
- groups.append(current_group)
182
- print(f"DEBUG: Saved group '{current_group.get('subheading_clean', 'Unknown')[:30]}...' with {len(current_group['content_lines'])} content lines")
183
-
184
- # Clean markdown symbols BEFORE determining type and storing
185
- clean_text = line
186
- heading_type = 'text'
187
-
188
- if line.startswith('# '):
189
- clean_text = line[2:].strip() # Remove "# "
190
- heading_type = 'h1'
191
- elif line.startswith('## '):
192
- clean_text = line[3:].strip() # Remove "## "
193
- heading_type = 'h2'
194
- elif line.startswith('### '):
195
- clean_text = line[4:].strip() # Remove "### "
196
- heading_type = 'h3'
197
- elif line.startswith('#### '):
198
- clean_text = line[5:].strip() # Remove "#### "
199
- heading_type = 'h4'
200
- elif re.match(r'^\d+\.\d+\s+', line):
201
- clean_text = line.strip() # Keep numbered headings as-is
202
- heading_type = 'numbered'
203
- elif is_bold_text:
204
- # Remove ** markers from bold text
205
- clean_text = clean_markdown_from_line(line.strip())
206
- heading_type = 'bold'
207
- elif is_bold_list_item:
208
- # Extract the bold text from list item and treat as H3 subheading
209
- # Remove the list marker and ** markers
210
- clean_text = re.sub(r'^[-*]\s*\*\*(.*?)\*\*', r'\1', line.strip())
211
- heading_type = 'h3' # Treat as H3 subheading
212
-
213
- print(f"DEBUG: Creating new group '{clean_text[:30]}...' (type: {heading_type})")
214
-
215
- # Start new group with cleaned text
216
- current_group = {
217
- 'subheading': line, # Keep original for reference
218
- 'content_lines': [],
219
- 'content': '',
220
- 'subheading_clean': clean_text, # Pre-cleaned text
221
- 'subheading_type': heading_type
222
- }
223
- else:
224
- # Add to current group's content - clean the line
225
- if current_group:
226
- cleaned_line = clean_markdown_from_line(line)
227
- current_group['content_lines'].append(cleaned_line)
228
- if i < 5: # Log first few content lines
229
- print(f"DEBUG: Added content line to group '{current_group.get('subheading_clean', 'Unknown')[:30]}...': '{cleaned_line[:50]}...'")
230
- else:
231
- # Content before any subheading - create a default group
232
- if not groups or groups[-1].get('subheading') != '## Introduction':
233
- cleaned_line = clean_markdown_from_line(line)
234
- current_group = {
235
- 'subheading': '## Introduction',
236
- 'content_lines': [cleaned_line],
237
- 'content': '',
238
- 'subheading_clean': 'Introduction',
239
- 'subheading_type': 'h2'
240
- }
241
-
242
- # Don't forget the last group
243
- if current_group:
244
- current_group['content'] = '\n'.join(current_group['content_lines'])
245
- groups.append(current_group)
246
- print(f"DEBUG: Saved final group '{current_group.get('subheading_clean', 'Unknown')[:30]}...' with {len(current_group['content_lines'])} content lines")
247
-
248
- # Add metadata to each group
249
- for i, group in enumerate(groups):
250
- group['group_id'] = f"group_{i}"
251
- group['content_length'] = len(group['content'])
252
- group['word_count'] = len(group['content'].split()) if group['content'] else 0
253
- group['has_charts'] = 'CHART' in group['content'].upper() if group['content'] else False
254
- group['line_count'] = len([line for line in group['content'].split('\n') if line.strip()])
255
-
256
- print(f"DEBUG: Created {len(groups)} groups total")
257
- return groups
258
-
259
- def extract_key_phrases(content: str) -> List[tuple]:
260
- """Extract key phrases from content for grouping metadata"""
261
- if not content:
262
- return []
263
-
264
- # Simple key phrase extraction
265
- words = re.findall(r'\b\w+\b', content.lower())
266
- word_freq = {}
267
-
268
- for word in words:
269
- if len(word) > 3: # Only words longer than 3 characters
270
- word_freq[word] = word_freq.get(word, 0) + 1
271
-
272
- # Return top 5 most frequent words
273
- return sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:5]
274
-
275
- def get_content_groups_info(content: str) -> Dict[str, Any]:
276
- """Get detailed information about content groups for analysis and debugging"""
277
- if not content:
278
- return {"groups": [], "summary": {"total_groups": 0, "total_content_length": 0}}
279
-
280
- groups = parse_content_into_groups(content)
281
-
282
- # Add key phrases to each group
283
- for group in groups:
284
- group['key_phrases'] = extract_key_phrases(group['content'])
285
-
286
- # Calculate summary statistics
287
- total_content_length = sum(group['content_length'] for group in groups)
288
- total_word_count = sum(group['word_count'] for group in groups)
289
- groups_with_charts = sum(1 for group in groups if group['has_charts'])
290
-
291
- summary = {
292
- "total_groups": len(groups),
293
- "total_content_length": total_content_length,
294
- "total_word_count": total_word_count,
295
- "groups_with_charts": groups_with_charts,
296
- "average_group_size": total_content_length / len(groups) if groups else 0,
297
- "largest_group": max(groups, key=lambda g: g['content_length'])['subheading_clean'] if groups else None,
298
- "smallest_group": min(groups, key=lambda g: g['content_length'])['subheading_clean'] if groups else None
299
- }
300
-
301
- return {
302
- "groups": groups,
303
- "summary": summary
304
- }