- 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
109 lines
4.0 KiB
Python
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")
|