homepage/content/blog/2022/10-28-CLRG-Results-Analysis/feisworx.mjs

154 lines
4.5 KiB
JavaScript

/**
* FeisWorx parser
*
* This is the output of Adobe Reader saving the PDF as XML.
*/
/**
* @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 FeisWorx 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
for (let rowIndex = 0; rowIndex < rawData.length; rowIndex++) {
let cells = rawData[rowIndex]
// Is it a page heading?
if ((cells.length >= 11) && (cells[0].trim().toLowerCase().startsWith("place"))) {
if (numRounds == 0) {
for (let cell of cells) {
if (cell.toLowerCase().startsWith("round")) {
numRounds++
}
}
}
continue
}
if (adjudicators.length == 0) {
let fishy = false
for (let adjudicator of cells) {
if (Number(adjudicator) > 0) {
fishy = true
}
adjudicators.push(adjudicator.trim())
}
if (fishy) {
console.warn("Adjudicators row doesn't look right", cells)
}
adjudicatorsPerRound = adjudicators.length / numRounds
if (! Number.isSafeInteger(adjudicatorsPerRound)) {
console.error(`Irrational number of adjudicators for number of rounds: (${adjudicators.length}/${numRounds})`)
}
continue
}
// Is this just a list of adjudicators again?
if (cells.length >= adjudicators.length) {
let lenDiff = cells.length - adjudicators.length
let same = true
for (let i = adjudicators.length-1; i >= 0; i--) {
if (adjudicators[i] != cells[i+lenDiff].trim()) {
same = false
break
}
}
if (same) {
continue
}
}
let row = {}
{
let parts = cells[0].trim().split(/\s+/)
row.overallRank = Number(parts[0])
row.overallPoints = Number(parts[1])
}
{
let match = cells[1].trim().match(/(\d+) - (.+) \((.+) *\)[ -]*(.+)?/)
if (match) {
row.number = Number(match[1])
row.name = match[2]
row.school = match[3]
row.qualifier = match[4]
}
}
/** @type {Round} */
let round = []
/** @type {Array.<Round>} */
row.rounds = []
for (let cellIndex = 2; cellIndex < cells.length; cellIndex++) {
let cell = cells[cellIndex]
/** @type {Adjudication} */
let adjudication = {}
let parts = cell.trim().split(/ - ?|\s/)
adjudication.adjudicator = adjudicators[cellIndex - 2]
if ((parts.length == 5) && (parts[3] == "AP")) {
parts.splice(3, 0, "NaN")
}
if ((parts.length == 7) && (parts[4] == "T")) {
adjudication.tie = true
parts.splice(4, 1)
} else {
adjudication.tie = false
}
if (parts.length != 6) {
console.error(`Wrong number of fields in row ${rowIndex} cell ${cellIndex}:`, parts, cells)
break
}
for (let i = 0; i < parts.length; i += 2) {
let key = parts[i]
let val = Number(parts[i+1])
switch (key) {
case "Raw":
adjudication.raw = val
break
case "Plc":
adjudication.placing = val
break
case "AP":
adjudication.points = val
break
default:
console.error(`Unknown key ${key} in row ${rowIndex} cell ${cellIndex}:`, cell)
break
}
}
round.push(adjudication)
if (round.length == adjudicatorsPerRound) {
row.rounds.push(round)
round = []
}
}
results.push(row)
}
return results
}
export {
parse
}