moth

Monarch Of The Hill game server
git clone https://git.woozle.org/neale/moth.git

moth / theme / reports
Neale Pickett  ·  2023-09-14

ksa.mjs

  1import * as moth from "../moth.mjs"
  2import * as common from "../common.mjs"
  3
  4const server = new moth.Server("../")
  5
  6/**
  7 * Update "doing" indicators
  8 * 
  9 * @param {String | null} what Text to display, or null to not update text
 10 * @param {Number | null} finished Percentage complete to display, or null to not update progress
 11 */
 12function doing(what, finished = null) {
 13    for (let e of document.querySelectorAll(".doing")) {
 14        e.classList.remove("hidden")
 15        if (what) {
 16            e.textContent = what
 17        }
 18        if (finished) {
 19            e.value = finished
 20        } else {
 21            e.removeAttribute("value")
 22        }
 23    }
 24}
 25function done() {
 26    for (let e of document.querySelectorAll(".doing")) {
 27        e.classList.add("hidden")
 28    }
 29}
 30
 31async function GetNice() {
 32    let NiceElementsByIdentifier = {}
 33    let resp = await fetch("NICEFramework2017.json")
 34    let obj = await resp.json()
 35    for (let e of obj.elements) {
 36        NiceElementsByIdentifier[e.element_identifier] = e
 37    }
 38    return NiceElementsByIdentifier
 39}
 40
 41/**
 42 * Fetch a puzzle, and fill its KSAs and rows.
 43 *
 44 * This is done once per puzzle, in an asynchronous function, allowing the
 45 * application to perform multiple blocking operations simultaneously.
 46 */
 47async function FetchAndFill(puzzle, KSAs, rows) {
 48    try {
 49        await puzzle.Populate()
 50    }
 51    catch (error) {
 52        // Keep on going with whatever Populate was able to fill
 53    }
 54    for (let KSA of (puzzle.KSAs || [])) {
 55        KSAs.add(KSA)
 56    }
 57
 58    for (let row of rows) {
 59        row.querySelector(".category").textContent = puzzle.Category
 60        row.querySelector(".points").textContent = puzzle.Points
 61        row.querySelector(".ksas").textContent = (puzzle.KSAs || []).join(" ")
 62        row.querySelector(".error").textContent = puzzle.Error.Body
 63    }
 64}
 65
 66async function init() {
 67    doing("Fetching NICE framework data")
 68    let nicePromise = GetNice()
 69
 70    doing("Retrieving server state")
 71    let state = await server.GetState()
 72
 73    doing("Retrieving all puzzles")
 74    let KSAsByCategory = {}
 75    let puzzlerowTemplate = document.querySelector("template#puzzlerow")
 76    let puzzles = state.Puzzles()
 77    let promises = []
 78    for (let category of state.Categories()) {
 79        KSAsByCategory[category] = new Set()
 80    }
 81    let pending = puzzles.length
 82    for (let puzzle of puzzles) {
 83        // Make space in the table, so everything fills in sorted order
 84        let rows = []
 85        for (let tbody of document.querySelectorAll("tbody")) {
 86            let row = puzzlerowTemplate.content.cloneNode(true).firstElementChild
 87            tbody.appendChild(row)
 88            rows.push(row)
 89        }
 90
 91        // Queue up a fetch, and update progress bar
 92        let promise = FetchAndFill(puzzle, KSAsByCategory[puzzle.Category], rows)
 93        promises.push(promise)
 94        promise.then(() => doing(null, 1 - (--pending / puzzles.length)))
 95
 96        if (promises.length > 50) {
 97            // Chrome runs out of resources if you queue up too many of these at once
 98            await Promise.all(promises)
 99            promises = []
100        }
101    }
102    await Promise.all(promises)
103
104    doing("Retrieving NICE identifiers")
105    let NiceElementsByIdentifier = await nicePromise
106
107
108    doing("Filling KSAs By Category")
109    let allKSAs = new Set()
110    for (let div of document.querySelectorAll(".KSAsByCategory")) {
111        for (let category of state.Categories()) {
112            doing(`Filling KSAs for category: ${category}`)
113            let KSAs = [...KSAsByCategory[category]]
114            KSAs.sort()
115
116            div.appendChild(document.createElement("h3")).textContent = category
117            let ul = div.appendChild(document.createElement("ul"))
118            for (let k of KSAs) {
119                let ksa = k.split(/\s+/)[0]
120                let ne = NiceElementsByIdentifier[ksa] || { text: "???" }
121                let text = `${ksa}: ${ne.text}`
122                ul.appendChild(document.createElement("li")).textContent = text
123                allKSAs.add(text)
124            }
125        }
126    }
127
128    doing("Filling KSAs")
129    for (let e of document.querySelectorAll(".allKSAs")) {
130        let KSAs = [...allKSAs]
131        KSAs.sort()
132        for (let text of KSAs) {
133            e.appendChild(document.createElement("li")).textContent = text
134        }
135    }
136
137    done()
138}
139
140common.WhenDOMLoaded(init)