Coverage for src/lexigram/admin/services/export/adapters/csv.py: 100%
27 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 14:23 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-21 14:23 +0800
1from __future__ import annotations
3import csv
4from datetime import UTC
5import io
6from typing import TYPE_CHECKING, Any
8from lexigram.admin.services.export.sanitize import sanitize_cell_value
9from lexigram.admin.services.export.service import IExportBackend
11if TYPE_CHECKING:
12 from lexigram.admin.services.export.scheduler import ExportJob
15class CsvExportBackend(IExportBackend):
16 """Backend for CSV exports."""
18 async def generate_file(
19 self,
20 job: ExportJob,
21 data: list[dict[str, Any]],
22 storage: Any,
23 export_dir: str,
24 ) -> str:
25 """Export data to CSV format."""
26 from datetime import datetime
28 timestamp = datetime.now(UTC).strftime("%Y%m%d_%H%M%S")
29 filename = f"{job.resource_name}_export_{timestamp}.csv"
30 file_path = f"{export_dir}/{filename}"
32 output = io.StringIO()
34 if data:
35 # Use specified columns or all columns
36 fieldnames = job.columns or list(data[0].keys())
38 writer = csv.DictWriter(output, fieldnames=fieldnames)
39 writer.writeheader()
41 for row in data:
42 # Filter to specified columns if provided
43 if job.columns:
44 filtered_row = {k: v for k, v in row.items() if k in job.columns}
45 else:
46 filtered_row = row
47 sanitized_row = {
48 k: sanitize_cell_value(v) for k, v in filtered_row.items()
49 }
50 writer.writerow(sanitized_row)
52 # Write to storage
53 content = output.getvalue()
54 await storage.upload(
55 file_path, content.encode("utf-8"), content_type="text/csv"
56 )
58 return file_path