Coverage for app\services\export_service.py: 25%
85 statements
« prev ^ index » next coverage.py v7.3.2, created at 2026-01-15 13:39 +0300
« prev ^ index » next coverage.py v7.3.2, created at 2026-01-15 13:39 +0300
1import csv
2import os
3import json
4import tempfile
5from datetime import datetime, UTC
6from typing import List, Dict, Any, Optional
7from sqlalchemy.orm import Session
8try:
9 import pandas as pd
10except ImportError:
11 pd = None
12from reportlab.lib import colors
13from reportlab.lib.pagesizes import letter, A4, landscape
14from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer
15from reportlab.lib.styles import getSampleStyleSheet
17from ..db import User, School, AuditLog, SchoolMetric, DataExport
19class ExportService:
20 def __init__(self, db: Session):
21 self.db = db
22 self.temp_dir = tempfile.gettempdir()
23 if not os.path.exists(self.temp_dir):
24 os.makedirs(self.temp_dir, exist_ok=True)
26 def generate_export(self, export_record: DataExport) -> str:
27 """
28 Generates the export file and returns the file path.
29 """
30 data = self._fetch_data(export_record.export_type, export_record.filters)
32 filename = f"export_{export_record.export_type}_{datetime.now(UTC).strftime('%Y%m%d%H%M%S')}"
33 file_path = os.path.join(self.temp_dir, f"{filename}.{export_record.format}")
35 if export_record.format == "csv":
36 self._write_csv(data, file_path)
37 elif export_record.format == "xlsx":
38 self._write_excel(data, file_path)
39 elif export_record.format == "pdf":
40 self._write_pdf(data, file_path, title=f"Export: {export_record.export_type}")
41 else:
42 raise ValueError(f"Unsupported format: {export_record.format}")
44 return file_path
46 def _fetch_data(self, export_type: str, filters: Optional[Dict[str, Any]]) -> List[Dict[str, Any]]:
47 if export_type == "users":
48 query = self.db.query(User)
49 # Apply filters if needed
50 users = query.all()
51 return [
52 {
53 "id": u.id,
54 "email": u.email,
55 "full_name": u.full_name,
56 "role": u.role.value if u.role else None,
57 "status": u.status.value if u.status else None,
58 "created_at": u.created_at.isoformat() if u.created_at else None
59 }
60 for u in users
61 ]
62 elif export_type == "schools":
63 query = self.db.query(School)
64 schools = query.all()
65 return [
66 {
67 "id": s.id,
68 "name": s.name,
69 "status": s.status,
70 "director": s.director_name,
71 "created_at": s.created_at.isoformat() if s.created_at else None
72 }
73 for s in schools
74 ]
75 elif export_type == "audit_logs":
76 query = self.db.query(AuditLog).order_by(AuditLog.created_at.desc()).limit(1000)
77 logs = query.all()
78 return [
79 {
80 "id": l.id,
81 "actor_id": l.actor_id,
82 "action": l.action.value if l.action else None,
83 "entity": l.entity,
84 "entity_id": l.entity_id,
85 "created_at": l.created_at.isoformat() if l.created_at else None
86 }
87 for l in logs
88 ]
89 else:
90 return []
92 def _write_csv(self, data: List[Dict[str, Any]], file_path: str):
93 if not data:
94 with open(file_path, 'w', newline='') as f:
95 pass
96 return
98 keys = data[0].keys()
99 with open(file_path, 'w', newline='', encoding='utf-8') as f:
100 dict_writer = csv.DictWriter(f, fieldnames=keys)
101 dict_writer.writeheader()
102 dict_writer.writerows(data)
104 def _write_excel(self, data: List[Dict[str, Any]], file_path: str):
105 if pd is None:
106 raise ImportError("pandas is not installed. Excel export is unavailable.")
108 if not data:
109 df = pd.DataFrame()
110 else:
111 df = pd.DataFrame(data)
113 df.to_excel(file_path, index=False)
115 def _write_pdf(self, data: List[Dict[str, Any]], file_path: str, title: str = "Report"):
116 doc = SimpleDocTemplate(file_path, pagesize=landscape(A4))
117 elements = []
118 styles = getSampleStyleSheet()
120 # Title
121 elements.append(Paragraph(title, styles['Title']))
122 elements.append(Spacer(1, 12))
124 if not data:
125 elements.append(Paragraph("No data available", styles['Normal']))
126 doc.build(elements)
127 return
129 # Table Data
130 keys = list(data[0].keys())
131 table_data = [keys] # Header
133 for item in data:
134 row = [str(item.get(key, "")) for key in keys]
135 table_data.append(row)
137 # Table Style
138 table = Table(table_data)
139 style = TableStyle([
140 ('BACKGROUND', (0, 0), (-1, 0), colors.grey),
141 ('TEXTCOLOR', (0, 0), (-1, 0), colors.whitesmoke),
142 ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
143 ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
144 ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
145 ('BACKGROUND', (0, 1), (-1, -1), colors.beige),
146 ('GRID', (0, 0), (-1, -1), 1, colors.black),
147 ])
148 table.setStyle(style)
150 elements.append(table)
151 doc.build(elements)