# ============================================================================== # Formeleon - Multi-tenant Form Generator # Author: Björn Runden # Module: services/exporter.py # Description: Service for exporting form submissions. # ============================================================================== import csv import json import io from typing import List, Dict, Any from flask import Response from models import Submission class ExporterService: """Service to handle generating exports for submissions.""" @classmethod def generate_csv_response(cls, submissions: List[Submission], filename: str = "export.csv") -> Response: """ Generates a Flask Response containing a CSV stream of the given submissions. """ # Determine all possible fields from the payloads all_keys = set() for sub in submissions: if isinstance(sub.payload, dict): all_keys.update(sub.payload.keys()) fieldnames = ['ID', 'Date', 'Form', 'Status', 'reCAPTCHA'] + sorted(list(all_keys)) si = io.StringIO() writer = csv.DictWriter(si, fieldnames=fieldnames) writer.writeheader() for sub in submissions: row = { 'ID': sub.id, 'Date': sub.created_at.strftime('%Y-%m-%d %H:%M:%S'), 'Form': sub.form_config.name if sub.form_config else 'Unknown', 'Status': sub.status.value, 'reCAPTCHA': sub.recaptcha_score if sub.recaptcha_score is not None else '' } if isinstance(sub.payload, dict): row.update(sub.payload) writer.writerow(row) output = si.getvalue() return Response( output, mimetype="text/csv", headers={"Content-disposition": f"attachment; filename={filename}"} ) @classmethod def generate_json_response(cls, submissions: List[Submission], filename: str = "export.json") -> Response: """ Generates a Flask Response containing a JSON array of the given submissions. """ data = [] for sub in submissions: data.append({ 'id': sub.id, 'date': sub.created_at.isoformat(), 'form_name': sub.form_config.name if sub.form_config else None, 'status': sub.status.value, 'recaptcha_score': sub.recaptcha_score, 'payload': sub.payload }) return Response( json.dumps(data, indent=2), mimetype="application/json", headers={"Content-disposition": f"attachment; filename={filename}"} )