// lib/features/igel/data/export_service.dart import 'dart:convert'; import 'dart:typed_data'; import 'package:file_saver/file_saver.dart'; import 'package:intl/intl.dart'; import '../../igel/data/igel_repository.dart'; import '../../messwerte/data/messwerte_repository.dart'; import '../../messwerte/domain/messwert.dart'; class IgelExportService { final IgelRepository igelRepo; final MesswerteRepository messRepo; IgelExportService({ required this.igelRepo, required this.messRepo, }); String _fmtDate(DateTime? d) => d == null ? '' : DateFormat('yyyy-MM-dd').format(d); String _fmtDateTime(DateTime? d) => d == null ? '' : DateFormat('yyyy-MM-dd HH:mm').format(d); String _csvEscape(String? v, {String delimiter = ';'}) { final s = v ?? ''; final needsQuote = s.contains(delimiter) || s.contains('\n') || s.contains('"'); final escaped = s.replaceAll('"', '""'); return needsQuote ? '"$escaped"' : escaped; } /// Erzeugt eine *denormalisierte* CSV: /// Jede Zeile = 1 Messwert + zugehörige Igel-Stammdaten. /// Hat ein Igel *keine* Messwerte, gibt es *trotzdem eine Zeile* mit leeren Messwert-Spalten. Future exportDenormalizedCsv() async { // Immer frisch laden final igelList = await igelRepo.list(); const sep = ';'; final sb = StringBuffer(); // Kopfzeile – nur Felder, die deine Models wirklich haben sb.writeln([ // Igel 'igel_id', 'igel_name', 'igel_gender', 'igel_feature', 'igel_note', 'igel_rescued_at', 'igel_location', 'igel_marking', 'igel_finder', 'igel_phone', 'igel_email', 'igel_admission_reason', 'igel_animal_condition', 'igel_released_at', 'igel_release_location', // Messwert 'messwert_id', 'messwert_datum', 'messwert_gewicht', 'messwert_behandlung', 'messwert_bemerkung', ].join(sep)); for (final ig in igelList) { List mw = []; try { mw = await messRepo.list(ig.id); } catch (_) { mw = []; } if (mw.isEmpty) { // Igel ohne Messwerte → eine Zeile mit leeren Messwert-Spalten sb.writeln([ ig.id, _csvEscape(ig.name, delimiter: sep), _csvEscape(ig.gender, delimiter: sep), _csvEscape(ig.feature, delimiter: sep), _csvEscape(ig.note, delimiter: sep), _fmtDate(ig.rescuedAt), _csvEscape(ig.location, delimiter: sep), _csvEscape(ig.marking, delimiter: sep), _csvEscape(ig.finder, delimiter: sep), _csvEscape(ig.phone, delimiter: sep), _csvEscape(ig.email, delimiter: sep), _csvEscape(ig.admissionReason, delimiter: sep), _csvEscape(ig.animalCondition, delimiter: sep), _fmtDate(ig.releasedAt), _csvEscape(ig.releaseLocation, delimiter: sep), // Messwert-Spalten leer '', '', '', '', '', ].join(sep)); continue; } for (final m in mw) { sb.writeln([ ig.id, _csvEscape(ig.name, delimiter: sep), _csvEscape(ig.gender, delimiter: sep), _csvEscape(ig.feature, delimiter: sep), _csvEscape(ig.note, delimiter: sep), _fmtDate(ig.rescuedAt), _csvEscape(ig.location, delimiter: sep), _csvEscape(ig.marking, delimiter: sep), _csvEscape(ig.finder, delimiter: sep), _csvEscape(ig.phone, delimiter: sep), _csvEscape(ig.email, delimiter: sep), _csvEscape(ig.admissionReason, delimiter: sep), _csvEscape(ig.animalCondition, delimiter: sep), _fmtDate(ig.releasedAt), _csvEscape(ig.releaseLocation, delimiter: sep), m.id, _fmtDateTime(m.datum), m.gewicht, _csvEscape(m.behandlung, delimiter: sep), _csvEscape(m.bemerkung, delimiter: sep), ].join(sep)); } } final bytes = Uint8List.fromList(const Utf8Encoder().convert(sb.toString())); final filename = 'igel_export_denorm_${DateFormat('yyyyMMdd_HHmmss').format(DateTime.now())}'; await FileSaver.instance.saveFile( name: filename, bytes: bytes, ext: 'csv', mimeType: MimeType.csv, ); } }