LCOV - code coverage report
Current view: | top level - lib/utils - event_excel_parser.dart |  |  | Coverage | Total | Hit
Test: | lcov.info |  | Lines: | 0.0 % | 51 | 0
Test Date: | 2025-08-12 03:21:51 |  | Functions: | - | 0 | 0

                Line data    Source code
           1              : import 'package:excel/excel.dart';
           2              :
           3              : /// 엑셀 파일에서 이벤트 데이터를 파싱하는 유틸리티 클래스
           4              : class EventExcelParser {
           5              :   /// 엑셀 파일 바이트 데이터에서 이벤트 데이터 목록을 추출합니다.
           6              :   ///
           7              :   /// [bytes] 엑셀 파일의 바이트 데이터
           8              :   ///
           9              :   /// 반환값:
          10              :   /// - processedRows: 처리된 총 행 수
          11              :   /// - validEvents: 유효한 이벤트 데이터 목록
          12              :   /// - invalidRows: 유효하지 않은 행 수
          13            0 :   static Map<String, dynamic> parseExcelBytes(List<int> bytes) {
          14            0 :     final excel = Excel.decodeBytes(bytes);
          15            0 :     final events = <Map<String, dynamic>>[];
          16              :     int invalidRows = 0;
          17              :     int processedRows = 0;
          18              :
          19              :     // 첫 번째 시트 사용
          20            0 :     final sheet = excel.tables.keys.first;
          21            0 :     final rows = excel.tables[sheet]?.rows;
          22              :
          23            0 :     if (rows == null || rows.isEmpty) {
          24            0 :       return {
          25              :         'processedRows': 0,
          26            0 :         'validEvents': [],
          27              :         'invalidRows': 0,
          28              :         'error': '엑셀 파일에 데이터가 없습니다.'
          29              :       };
          30              :     }
          31              :
          32              :     // 헤더 행 추출 (첫 번째 행)
          33            0 :     final headers = <String>[];
          34            0 :     for (final cell in rows[0]) {
          35            0 :       if (cell?.value != null) {
          36            0 :         headers.add(cell!.value.toString().trim().toLowerCase());
          37              :       }
          38              :     }
          39              :
          40              :     // 필수 필드 확인
          41            0 :     if (!headers.contains('title') || !headers.contains('startdate')) {
          42            0 :       return {
          43              :         'processedRows': 0,
          44            0 :         'validEvents': [],
          45              :         'invalidRows': 0,
          46              :         'error': '필수 필드(title, startDate)가 없습니다.'
          47              :       };
          48              :     }
          49              :
          50              :     // 데이터 행 처리 (헤더 제외)
          51            0 :     for (var i = 1; i < rows.length; i++) {
          52            0 :       processedRows++;
          53            0 :       final row = rows[i];
          54            0 :       final event = <String, dynamic>{};
          55              :
          56              :       // 각 셀 처리
          57            0 :       for (var j = 0; j < headers.length && j < row.length; j++) {
          58            0 :         final header = headers[j];
          59            0 :         final value = row[j]?.value;
          60              :
          61              :         // 날짜 필드 특별 처리
          62            0 :         if (header == 'startdate' || header == 'enddate') {
          63            0 :           DateTime? date = _parseDate(value);
          64              :
          65              :           // 날짜 값 설정
          66              :           if (date != null) {
          67              :             // 헤더 이름 정규화 (startdate -> startDate)
          68            0 :             final normalizedHeader = header == 'startdate' ? 'startDate' : 'endDate';
          69            0 :             event[normalizedHeader] = date.toIso8601String();
          70              :           } else {
          71              :             // 날짜 파싱 실패 시 startDate는 필수이므로 현재 날짜 사용
          72            0 :             if (header == 'startdate') {
          73            0 :               event['startDate'] = DateTime.now().toIso8601String();
          74              :             } else {
          75              :               // endDate는 선택적이므로 null 설정
          76            0 :               event['endDate'] = null;
          77              :             }
          78              :           }
          79              :         } else {
          80              :           // 문자열 필드의 경우 빈 문자열은 null로 처리
          81            0 :           final stringValue = value?.toString().trim() ?? '';
          82              :
          83              :           // 헤더 이름 정규화 (특별한 경우)
          84              :           String normalizedHeader = header;
          85            0 :           if (header == 'title' || header == 'description' || header == 'location' ||
          86            0 :               header == 'status' || header == 'type' || header == 'maxparticipants') {
          87              :             // 첫 글자를 소문자로 유지하고 나머지는 원래 형식 유지
          88            0 :             normalizedHeader = header == 'maxparticipants' ? 'maxParticipants' : header;
          89              :           }
          90              :
          91              :           // 빈 문자열은 null로 처리
          92            0 :           event[normalizedHeader] = stringValue.isEmpty ? null : stringValue;
          93              :         }
          94              :       }
          95              :
          96              :       // 필수 필드가 있는 경우만 추가
          97            0 :       if (event.containsKey('title') && event.containsKey('startDate')) {
          98            0 :         events.add(event);
          99              :       } else {
         100            0 :         invalidRows++;
         101              :       }
         102              :     }
         103              :
         104            0 :     return {
         105              :       'processedRows': processedRows,
         106              :       'validEvents': events,
         107              :       'invalidRows': invalidRows,
         108            0 :       'error': events.isEmpty ? '유효한 이벤트 데이터가 없습니다.' : null
         109              :     };
         110              :   }
         111              :
         112              :   /// 다양한 형식의 날짜 값을 DateTime 객체로 파싱합니다.
         113            0 :   static DateTime? _parseDate(dynamic value) {
         114              :     DateTime? date;
         115              :
         116              :     // DateTime 타입인 경우
         117            0 :     if (value is DateTime) {
         118              :       return value;
         119              :     }
         120              :
         121              :     // 문자열인 경우
         122              :     if (value != null) {
         123            0 :       final strValue = value.toString();
         124              :
         125            0 :       if (strValue.isNotEmpty) {
         126              :         // YYYY-MM-DD 형식 시도
         127              :         try {
         128            0 :           date = DateTime.parse(strValue);
         129              :         } catch (_) {
         130              :           // 실패 시 다른 형식 시도
         131              :         }
         132              :
         133              :         // YYYY/MM/DD 형식 시도
         134            0 :         if (date == null && strValue.contains('/')) {
         135              :           try {
         136            0 :             final parts = strValue.split('/');
         137            0 :             if (parts.length == 3) {
         138            0 :               date = DateTime(
         139            0 :                 int.parse(parts[0]),
         140            0 :                 int.parse(parts[1]),
         141            0 :                 int.parse(parts[2]),
         142              :               );
         143              :             }
         144              :           } catch (_) {
         145              :             // 실패 시 무시
         146              :           }
         147              :         }
         148              :       }
         149              :     }
         150              :
         151              :     return date;
         152              :   }
         153              : }

Generated by: LCOV version 2.3.1-1