myheats

Live heats, scoring and leaderboard for sport events
git clone https://git.in0rdr.ch/myheats.git
Log | Files | Refs | Pull requests | README | LICENSE

utils.js (4204B)


      1 const api_uri = import.meta.env.VITE_API_URI
      2 const api_port = import.meta.env.VITE_API_PORT
      3 
      4 export const exportHeatsToCSV = async function(e, { heats }) {
      5   e.preventDefault()
      6 
      7   // csv header
      8   let csv = "created_at,name,location,planned_start\n"
      9 
     10   // append athlete data
     11   for (let i = 0; i < heats.length; i++) {
     12     csv += heats[i].created_at + "," + heats[i].name+ "," + heats[i].location+ ","
     13       + heats[i].planned_start+ "\n"
     14   }
     15 
     16   exportCSV(csv, "heats")
     17 }
     18 
     19 export const exportAthletesToCSV = async function(e, { athletes }) {
     20   e.preventDefault()
     21 
     22   // csv header
     23   let csv = "created_at,nr,firstname,lastname,birthday,school\n"
     24 
     25   // append athlete data
     26   for (let i = 0; i < athletes.length; i++) {
     27     csv += athletes[i].created_at + "," + athletes[i].nr+ "," + athletes[i].firstname+ ","
     28       + athletes[i].lastname+ "," + athletes[i].birthday+ "," + athletes[i].school + "\n"
     29   }
     30 
     31   exportCSV(csv, "athletes")
     32 }
     33 
     34 export const exportLeaderboardToCSV = async function(e, leaderboard, heatSelection, rankingComp) {
     35   e.preventDefault()
     36 
     37   if (leaderboard.length === 0) {
     38     alert('Leaderboard is empty, nothing to export')
     39     return
     40   }
     41 
     42   // concatenate heat labels
     43   const heatNames = heatSelection.map(h => h.label)
     44 
     45   // rank leaderboard by selected comparator for each entry i in the board,
     46   // for every heat h
     47   const heatSummaries = leaderboard.sort(rankByHeat(rankingComp)).map(i =>
     48     heatSelection.map(h => formatScores(i, h))
     49   )
     50 
     51   // csv header
     52   let csv = "rank,start_nr,firstname,lastname,birthday,school,"
     53              + heatNames + "," + "best,worst,total\n"
     54 
     55   // append leaderboard score results
     56   for (let i = 0; i < leaderboard.length; i++) {
     57     csv += i+1 + "," + leaderboard[i].nr + "," + leaderboard[i].firstname + "," + leaderboard[i].lastname + ","
     58       + leaderboard[i].birthday + "," + leaderboard[i].school + ","
     59       + heatSummaries[i] + ","
     60       + leaderboard[i].bestHeat + "," + leaderboard[i].worstHeat + "," + leaderboard[i].sum + "\n"
     61   }
     62 
     63   exportCSV(csv, "scores")
     64 }
     65 
     66 // define the ranking logic
     67 export const rankByHeat = function(rankingComp) {
     68   return function(a, b) {
     69     // rank by chosen heat or ranking comparator
     70     for (const r of rankingComp) {
     71       switch(r.value) {
     72         case 'start':
     73           // rank by start number
     74           return b.nr < a.nr
     75         case 'best':
     76           if (b.bestHeat - a.bestHeat !== 0) {
     77             // rank by best heat first
     78             return b.bestHeat - a.bestHeat
     79           }
     80           // rank by least worst heat for identical best heats
     81           return b.worstHeat - a.worstHeat
     82         case 'worst':
     83           // rank by worst heat
     84           return b.worstHeat - a.worstHeat
     85         case 'total':
     86           // rank by total sum across heats
     87           return b.sum - a.sum
     88         default:
     89           // rank by heat totals
     90           let aHeatTotal = a.heats.find(h => h.heatId === r.value)?.summary
     91           let bHeatTotal = b.heats.find(h => h.heatId === r.value)?.summary
     92           return bHeatTotal - aHeatTotal
     93       }
     94     }
     95   }
     96 }
     97 
     98 // Scores concat with "+" for leaderboard entry i and heat h
     99 export const getScores = function(i, h) {
    100   const scores = i.heats.find(heat => heat.heatId === h.value)?.scores?.map(s => s.score.toFixed(1)).join(" + ")
    101   return scores
    102 }
    103 // Returns formatted string with summed scores and summary for leaderboard
    104 // entry i and heat h
    105 export const formatScores = function(i, h) {
    106   const scores = getScores(i, h)
    107   if (scores) {
    108     // get individual scores of the heat and score sum
    109     return getScores(i, h) + " = " + i.heats.find(heat => heat.heatId === h.value)?.summary.toFixed(1)
    110   } else {
    111     return NaN.toString()
    112   }
    113 }
    114 
    115 // export CSV as blob
    116 const exportCSV = function(csv, fileSlug) {
    117   // create blob from csv
    118   const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
    119 
    120   // download blob
    121   const url = URL.createObjectURL(blob)
    122   const link = document.createElement('a')
    123   link.href = url
    124   link.download = fileSlug + "-" + new Date().toISOString() + ".csv"
    125   document.body.appendChild(link)
    126   link.click()
    127   document.body.removeChild(link)
    128   // let browser know not to keep the reference to the file
    129   URL.revokeObjectURL(url)
    130 }