UNPKG

vex-match-parser

Version:

A TypeScript library for parsing VEX Robotics match schedules from PDF files. Works in both Node.js and browser environments.

240 lines (184 loc) 6.39 kB
# VEX Match Parser A TypeScript library for parsing VEX Robotics match schedules from PDF files. **Works in both Node.js and browser environments**. ## Features - ✅ **Universal**: Works in Node.js and browsers - ✅ Supports **3 VEX competition types**: VEXIQ, V5, and VEXGO - ✅ Parses both **Qualification (Q)** and **Practice (P)** matches - ✅ Automatic competition type detection - ✅ Full TypeScript support with type definitions - ✅ Simple, clean API ## Installation ```bash npm install vex-match-parser ``` ## Usage ### Node.js (File Path) ```typescript import { parseVEXMatchSchedule } from 'vex-match-parser'; // Parse from file path const schedule = await parseVEXMatchSchedule('./match-schedule.pdf'); console.log(schedule.type); // 'VEXIQ' | 'V5' | 'VEXGO' console.log(schedule.matchType); // 'Qualification' | 'Practice' console.log(schedule.event); // '南宁WRC-IQ初中' console.log(schedule.matches); // Array of matches ``` ### Browser (File Upload) ```html <!DOCTYPE html> <html> <head> <title>VEX Match Parser Demo</title> </head> <body> <input type="file" id="pdfInput" accept=".pdf" /> <div id="result"></div> <script type="module"> import { parseVEXMatchScheduleFromFile } from 'vex-match-parser'; document.getElementById('pdfInput').addEventListener('change', async (e) => { const file = e.target.files[0]; if (!file) return; try { const schedule = await parseVEXMatchScheduleFromFile(file); document.getElementById('result').innerHTML = ` <h2>${schedule.event}</h2> <p>Competition: ${schedule.type}</p> <p>Matches: ${schedule.matches.length}</p> <pre>${JSON.stringify(schedule.matches[0], null, 2)}</pre> `; } catch (error) { console.error('Error parsing PDF:', error); } }); </script> </body> </html> ``` ### Browser (ArrayBuffer) ```typescript import { parseVEXMatchSchedule } from 'vex-match-parser'; // From fetch const response = await fetch('/path/to/schedule.pdf'); const arrayBuffer = await response.arrayBuffer(); const schedule = await parseVEXMatchSchedule(arrayBuffer, 'schedule.pdf'); // From File const file = document.querySelector('input[type="file"]').files[0]; const buffer = await file.arrayBuffer(); const schedule2 = await parseVEXMatchSchedule(buffer, file.name); ``` ### React Example ```tsx import { parseVEXMatchScheduleFromFile } from 'vex-match-parser'; import { useState } from 'react'; function MatchScheduleUploader() { const [schedule, setSchedule] = useState(null); const [loading, setLoading] = useState(false); const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (!file) return; setLoading(true); try { const parsedSchedule = await parseVEXMatchScheduleFromFile(file); setSchedule(parsedSchedule); } catch (error) { console.error('Error:', error); } finally { setLoading(false); } }; return ( <div> <input type="file" onChange={handleFileUpload} accept=".pdf" /> {loading && <p>Loading...</p>} {schedule && ( <div> <h2>{schedule.event}</h2> <p>Type: {schedule.type}</p> <p>Total Matches: {schedule.matches.length}</p> </div> )} </div> ); } ``` ## TypeScript Types ```typescript import { Match, MatchSchedule, CompetitionType } from 'vex-match-parser'; // Competition types type CompetitionType = 'VEXIQ' | 'V5' | 'VEXGO'; type MatchType = 'Qualification' | 'Practice'; // Match schedule structure interface MatchSchedule { title: string; // "Qualification Match List" matchType: MatchType; // "Qualification" or "Practice" event: string; // Event name division: string; // Division name type: CompetitionType; // VEXIQ/V5/VEXGO matches: Match[]; // Array of matches filename: string; // Original PDF filename } // Match types (union type) type Match = VEXIQMatch | V5Match | VEXGOMatch; // VEXIQ/VEXGO Match (2 teams) interface VEXIQMatch { type: 'VEXIQ'; matchNumber: string; // "Q1", "P5", etc. matchType: MatchType; field: string; // "A", "B", etc. time: string; // "周四 10:00 AM" team1: string; // "7645B" team2: string; // "7323W" } // V5 Match (4 teams, red vs blue alliance) interface V5Match { type: 'V5'; matchNumber: string; matchType: MatchType; field: string; time: string; red1: string; // "23456W" red2: string; // "66666B" blue1: string; // "1023K" blue2: string; // "900K" } ``` ## API Reference ### `parseVEXMatchSchedule(data, filename?)` Main parsing function. Works in both Node.js and browser. **Parameters:** - `data`: `string | ArrayBuffer | Uint8Array` - Node.js: File path as string - Browser: ArrayBuffer or Uint8Array - `filename?`: `string` (optional) - Original filename for type detection **Returns:** `Promise<MatchSchedule>` ### `parseVEXMatchScheduleFromFile(file)` Browser-specific helper for File objects. **Parameters:** - `file`: `File` - File object from `<input type="file">` **Returns:** `Promise<MatchSchedule>` ## How It Works The parser uses a three-layer detection system to automatically identify the competition type: 1. **Filename detection**: Checks if filename contains "V5", "IQ", or "GO" 2. **Header detection**: Checks the PDF title for competition type 3. **Team count detection**: V5 has 4 teams per match, IQ/GO have 2 teams ## Supported PDF Formats The parser expects VEX match schedule PDFs with the following structure: - **Title**: `Qualification Match List` or `Practice Match List` - **Subtitle**: Event name and division (e.g., `南宁WRC-IQ初中 - Default Division`) - **Table columns**: - VEXIQ/VEXGO: `Match | Field | Time | Team 1 | Team 2` - V5: `Match | Field | Time | Red 1 | Red 2 | Blue 1 | Blue 2` ## Development ```bash # Install dependencies npm install # Build npm run build # Test npm test ``` ## Browser Compatibility - Modern browsers with ES modules support - Requires support for: `ArrayBuffer`, `Uint8Array`, `File` API - Works with: Chrome 63+, Firefox 60+, Safari 11.1+, Edge 79+ ## License MIT