Files
mtgonline/backend/app/services/file_parser.py
T
akadmin 1e7c762452 Complete Phase 2: Card import, deck building, and full API
- Add card import feature with fuzzy matching
- Implement deck CRUD and management endpoints
- Add user data APIs for groups, networks, preferences, activity, replays
- Create comprehensive API documentation (API_DOCUMENTATION.md)
- Add ENDPOINT_AUDIT.md for endpoint verification
- Update documentation (README, ROADMAP, state.json)
- Update architecture blueprint and Cockatrice analysis
- All Phase 2 deliverables complete and documented
2026-07-25 02:56:29 +00:00

109 lines
4.0 KiB
Python

"""File parser service for card import."""
import csv
import json
from typing import List, Union
from pathlib import Path
import openpyxl
import pandas as pd
class FileParser:
"""Parse various file formats for card import."""
SUPPORTED_FORMATS = ['xlsx', 'csv', 'json', 'ods']
@staticmethod
async def parse_file(file_path: Path) -> List[str]:
"""
Parse a file and extract card names.
Args:
file_path: Path to the file to parse
Returns:
List of card names extracted from the file
Raises:
ValueError: If file format is not supported
FileNotFoundError: If file does not exist
Exception: If file cannot be parsed
"""
file_type = file_path.suffix.lower().lstrip('.')
if file_type not in FileParser.SUPPORTED_FORMATS:
raise ValueError(f"Unsupported file format: {file_type}. Supported formats: {FileParser.SUPPORTED_FORMATS}")
if not file_path.exists():
raise FileNotFoundError(f"File not found: {file_path}")
if file_type == 'csv':
return FileParser._parse_csv(file_path)
elif file_type == 'json':
return FileParser._parse_json(file_path)
elif file_type == 'xlsx':
return FileParser._parse_xlsx(file_path)
elif file_type == 'ods':
return FileParser._parse_ods(file_path)
@staticmethod
def _parse_csv(file_path: Path) -> List[str]:
"""Parse CSV file and extract card names."""
card_names = []
with open(file_path, 'r', encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
# Take first non-empty column as card name
for cell in row:
cell = cell.strip()
if cell:
card_names.append(cell)
break
return card_names
@staticmethod
def _parse_json(file_path: Path) -> List[str]:
"""Parse JSON file and extract card names."""
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
if isinstance(data, list):
return [str(item).strip() for item in data if str(item).strip()]
elif isinstance(data, dict):
# Try common keys
for key in ['cards', 'card_names', 'cards_list', 'list']:
if key in data and isinstance(data[key], list):
return [str(item).strip() for item in data[key] if str(item).strip()]
# If no common key found, try first list value
for value in data.values():
if isinstance(value, list):
return [str(item).strip() for item in value if str(item).strip()]
raise ValueError("Invalid JSON format: expected list or dict with card names")
@staticmethod
def _parse_xlsx(file_path: Path) -> List[str]:
"""Parse XLSX file and extract card names from first column."""
card_names = []
try:
workbook = openpyxl.load_workbook(file_path, read_only=True)
worksheet = workbook.active
for row in worksheet.iter_rows(values_only=True):
if row and row[0]:
cell_value = str(row[0]).strip()
if cell_value:
card_names.append(cell_value)
finally:
if 'workbook' in locals():
workbook.close()
return card_names
@staticmethod
def _parse_ods(file_path: Path) -> List[str]:
"""Parse ODS file and extract card names from first column."""
try:
df = pd.read_excel(file_path, engine='odf')
card_names = df.iloc[:, 0].dropna().astype(str).str.strip().tolist()
return [name for name in card_names if name]
except ImportError:
raise ImportError("pandas with odf engine required for ODS parsing. Install with: pip install pandas odfpy")