| 128 |
|
"""Total number of field-level rows.""" |
| 129 |
|
return len(self.rows) |
| 130 |
|
|
| 131 |
+ |
|
| 132 |
+ |
## This works well but it's too slow on reallly large reports... |
| 133 |
|
def export_to_excel(self, excel_path: str) -> None: |
| 134 |
|
""" |
| 135 |
|
Export flattened rows to Excel using win32com. |
| 176 |
|
wb.Close(SaveChanges=True) |
| 177 |
|
xlApp.Quit() |
| 178 |
|
|
| 179 |
+ |
## Still using the Excel win32com API but as a bulk write, which is more complicated but faster. |
| 180 |
+ |
def export_to_excel_fast(self, excel_path: str) -> None: |
| 181 |
+ |
""" |
| 182 |
+ |
Export flattened rows to Excel using win32com, but in a single |
| 183 |
+ |
bulk write for speed. |
| 184 |
+ |
""" |
| 185 |
+ |
from win32com.client import Dispatch, constants |
| 186 |
+ |
|
| 187 |
+ |
headers = self.BASE_COLUMNS + self.DETAIL_COLUMNS |
| 188 |
+ |
n_rows = len(self.rows) |
| 189 |
+ |
n_cols = len(headers) |
| 190 |
+ |
|
| 191 |
+ |
# Build a 2D list: first row = headers, rest = data |
| 192 |
+ |
data_matrix = [headers] |
| 193 |
+ |
for row in self.rows: |
| 194 |
+ |
data_matrix.append([row.get(h, "") for h in headers]) |
| 195 |
+ |
|
| 196 |
+ |
xlApp = Dispatch("Excel.Application") |
| 197 |
+ |
xlApp.Visible = False |
| 198 |
+ |
# Turn off some expensive stuff |
| 199 |
+ |
xlApp.ScreenUpdating = False |
| 200 |
+ |
xlApp.DisplayAlerts = False |
| 201 |
+ |
calc_original = xlApp.Calculation |
| 202 |
+ |
xlApp.Calculation = constants.xlCalculationManual |
| 203 |
+ |
|
| 204 |
+ |
try: |
| 205 |
+ |
wb = xlApp.Workbooks.Add() |
| 206 |
+ |
ws = wb.Worksheets(1) |
| 207 |
+ |
ws.Name = "Audit Changes" |
| 208 |
+ |
|
| 209 |
+ |
# Define the full range (including header row) |
| 210 |
+ |
top_left = ws.Cells(1, 1) |
| 211 |
+ |
bottom_right = ws.Cells(n_rows + 1, n_cols) |
| 212 |
+ |
data_range = ws.Range(top_left, bottom_right) |
| 213 |
+ |
|
| 214 |
+ |
# Bulk write: ONE COM call instead of millions |
| 215 |
+ |
data_range.Value = data_matrix |
| 216 |
+ |
|
| 217 |
+ |
# Autofit and optionally make a table |
| 218 |
+ |
data_range.EntireColumn.AutoFit() |
| 219 |
+ |
|
| 220 |
+ |
try: |
| 221 |
+ |
table = ws.ListObjects.Add( |
| 222 |
+ |
SourceType=0, # xlSrcRange |
| 223 |
+ |
Source=data_range, |
| 224 |
+ |
XlListObjectHasHeaders=1, # xlYes |
| 225 |
+ |
) |
| 226 |
+ |
table.Name = "AuditChangesTable" |
| 227 |
+ |
table.TableStyle = "TableStyleLight9" |
| 228 |
+ |
except Exception: |
| 229 |
+ |
# If ListObjects/TableStyle fails, ignore |
| 230 |
+ |
pass |
| 231 |
+ |
|
| 232 |
+ |
wb.SaveAs(excel_path) |
| 233 |
+ |
wb.Close(SaveChanges=True) |
| 234 |
+ |
finally: |
| 235 |
+ |
# Restore calculation mode and quit |
| 236 |
+ |
xlApp.Calculation = calc_original |
| 237 |
+ |
xlApp.ScreenUpdating = True |
| 238 |
+ |
xlApp.DisplayAlerts = True |
| 239 |
+ |
xlApp.Quit() |
| 240 |
+ |
|
| 241 |
+ |
## Here I'm trying without using Excel API at all. |
| 242 |
+ |
def export_to_csv(self, csv_path: str) -> None: |
| 243 |
+ |
""" |
| 244 |
+ |
Export flattened rows as a CSV file (very fast). |
| 245 |
+ |
Excel opens CSV directly. |
| 246 |
+ |
""" |
| 247 |
+ |
headers = self.BASE_COLUMNS + self.DETAIL_COLUMNS |
| 248 |
+ |
|
| 249 |
+ |
with open(csv_path, "w", newline="", encoding="utf-8-sig") as f: |
| 250 |
+ |
writer = csv.writer(f) |
| 251 |
+ |
writer.writerow(headers) |
| 252 |
+ |
for row in self.rows: |
| 253 |
+ |
writer.writerow([row.get(h, "") for h in headers]) |
| 254 |
+ |
|
| 255 |
|
|
| 256 |
|
def _copy_base_columns(self, src_row: Dict[str, Any]) -> Dict[str, Any]: |
| 257 |
|
""" |