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