116 lines
3.4 KiB
JavaScript
116 lines
3.4 KiB
JavaScript
/**
|
|
* Guidebook parser
|
|
*
|
|
* We're not actually sure what generated these PDFs.
|
|
* But we got them from Guidebook, so there you go.
|
|
*
|
|
* This is the output of Adobe Reader saving the PDF as XML.
|
|
*/
|
|
|
|
import {awardPoints, guessPlacing} from "./awardPoints.mjs"
|
|
|
|
/**
|
|
* @typedef {import("./types.mjs").Results} Results
|
|
* @typedef {import("./types.mjs").Result} Result
|
|
* @typedef {import("./types.mjs").Round} Round
|
|
* @typedef {import("./types.mjs").Adjudication} Adjudication
|
|
*/
|
|
|
|
|
|
/**
|
|
* Parse Guidebook data
|
|
*
|
|
* @param {Array.<Array.<String>>} rawData Raw data
|
|
* @returns {Results}
|
|
*/
|
|
function parse(rawData) {
|
|
/** @type {Results} */
|
|
let results = []
|
|
let adjudicators = []
|
|
let numRounds = 0
|
|
let adjudicatorsPerRound = 0
|
|
|
|
let possibleTiesByAdjudicatorRound = {}
|
|
|
|
for (let rowIndex = 0; rowIndex < rawData.length; rowIndex++) {
|
|
let cells = rawData[rowIndex]
|
|
|
|
// Is it a page heading?
|
|
if ((cells[0].trim().toLowerCase() == "card")) {
|
|
continue
|
|
}
|
|
|
|
// Is it a list of adjudicators?
|
|
if (cells[cells.length-1].trim().toLowerCase() == "total ip *") {
|
|
cells.splice(cells.length-1, 1) // -1: total IP *
|
|
cells.splice(0, 5) // 0 - 4: blank
|
|
adjudicators = []
|
|
for (let cell of cells) {
|
|
cell = cell.trim()
|
|
if (cell.toLowerCase().includes("rounds 1")) {
|
|
// skip it
|
|
} else if (cell.toLowerCase().includes("round total")) {
|
|
numRounds++
|
|
} else {
|
|
adjudicators.push(cell)
|
|
}
|
|
}
|
|
adjudicatorsPerRound = adjudicators.length / numRounds
|
|
if (! Number.isSafeInteger(adjudicatorsPerRound)) {
|
|
console.error(`Irrational number of adjudicators for number of rounds: (${adjudicators.length}/${numRounds})`)
|
|
}
|
|
continue
|
|
}
|
|
|
|
let row = {}
|
|
row.number = Number(cells[0])
|
|
// cells[1]: Position at recall
|
|
row.overallRank = Number(cells[2])
|
|
{
|
|
let parts = cells[3].trim().split(/\s:\s/)
|
|
console.log(parts, cells[3])
|
|
let nameSchool = parts[0]
|
|
// parts[1]: region
|
|
// We're going to take a wild-ass guess here that the dancer only has two names
|
|
let subparts = nameSchool.split(/\s+/)
|
|
row.name = subparts.slice(0, 2).join(" ")
|
|
row.school = subparts.slice(2).join(" ")
|
|
}
|
|
row.qualifier = cells[4].trim()
|
|
|
|
/** @type {Round} */
|
|
let round = []
|
|
/** @type {Array.<Round>} */
|
|
row.rounds = []
|
|
let adjudicatorNumber = 0
|
|
for (let cellIndex = 5; cellIndex < cells.length; cellIndex++) {
|
|
let cell = cells[cellIndex].trim()
|
|
if (! cell.includes("/")) {
|
|
continue
|
|
}
|
|
|
|
/** @type {Adjudication} */
|
|
let adjudication = {}
|
|
adjudication.adjudicator = adjudicators[adjudicatorNumber++]
|
|
|
|
let parts = cell.split("/")
|
|
adjudication.raw = Number(parts[0])
|
|
adjudication.points = Number(parts[1])
|
|
adjudication.placing = guessPlacing(adjudication.points)
|
|
// Guidebook reports don't list every dancer: we'll guess placing later
|
|
|
|
round.push(adjudication)
|
|
if (round.length == adjudicatorsPerRound) {
|
|
row.rounds.push(round)
|
|
round = []
|
|
}
|
|
}
|
|
results.push(row)
|
|
}
|
|
return results
|
|
}
|
|
|
|
export {
|
|
parse,
|
|
}
|
|
|