User:Habst/WorldAthletics2Wiki.js

/* handle ties e.g. in high jump, split points -- simply bail if sorted order is different than prescribed order? */ detailEvt = ``; hideCountry = false; doSort = false; bdCol = true; window.data ??= {}; window.cache ??= {}; (async => { for (const day of [1]) window.data[day]??=await (await fetch("https://lqf74cdt2jf7tktozacymhcvbe.appsync-api.eu-west-1.amazonaws.com/graphql", {  "headers": {    "x-api-key": "da2-jgtxe5f2tvcafinp5zh5kmq5oq" // note API key is intentionally public  },  "body": JSON.stringify({ "operationName": "getCalendarCompetitionResults", "variables": { "competitionId": 7174052, "day": day, "eventId": null }, "query": `query getCalendarCompetitionResults($competitionId: Int, $day: Int, $eventId: Int) { getCalendarCompetitionResults(competitionId: $competitionId, day: $day, eventId: $eventId) { competition { dateRange endDate name rankingCategory startDate venue __typename }   eventTitles { rankingCategory eventTitle events { event eventId gender isRelay perResultWind withWind summary { competitor { teamMembers { id             name iaafId urlSlug __typename }           id            name iaafId urlSlug birthDate __typename }         mark nationality placeInRace placeInRound points raceNumber records wind __typename }       races { date day race raceId raceNumber results { competitor { teamMembers { id               name iaafId urlSlug __typename }             id              name iaafId urlSlug birthDate hasProfile __typename }           mark nationality place points qualified records wind remark details { event eventId raceNumber mark wind placeInRound placeInRace points overallPoints placeInRoundByPoints overallPlaceByPoints __typename }           __typename }         startList { competitor { birthDate country id             name urlSlug __typename }           order pb           sb            bib __typename }         wind __typename }       __typename }     __typename }   options { days { date day __typename }     events { gender id       name combined __typename }     __typename }   parameters { competitionId day eventId __typename }   __typename } }` }), "method": "POST", })).json;

if (typeof nameFixer === 'undefined') { const script = Object.assign(document.createElement('script'), { src: 'https://unpkg.com/name-fixer@1.0.0' }); document.body.appendChild(script); await new Promise(res => script.addEventListener('load', res)); } titleExists=async (name)=>{ const enLabelTitleMatch = await fetch(`https://xtools.wmcloud.org/api/page/articleinfo/en.wikipedia.org/${name.replace('|', '')}?format=json&uselang=en`); return enLabelTitleMatch.status === 200; } getSuffix=async (name, evt, year) => { const el = evt?.toLowerCase ?? ''; const parens = evt?.includes('mH') || el.includes('metres hurdles') ? 'hurdler' : el.includes('high jump') ? 'high jumper' : el.includes('long jump') ? 'long jumper' : el.includes('pole vault') ? 'pole vaulter' : el.includes('triple jump') ? 'triple jumper' : el.includes('shot put') ? 'shot putter' : el.includes('discus') ? 'discus thrower' : el.includes('hammer') ? 'hammer thrower' : el.includes('javelin') ? 'javelin thrower' : el.includes('steeplchase') ? 'steeplechase runner' : ['60m', '60 m', '100m', '100 m', '200m', '200 m', '400m', '400 m'].some(d => el.includes(d)) ? 'sprinter' : 'runner'; name += ` (${parens})`; if (await titleExists(name)) name = name.replace(`(${parens})`, `(${parens}, born ${year})`); return name; } getTitle=async (id,name,evt,year)=>{ const words = name.split(' '); const lnameStart = words.findIndex(w => w.toUpperCase === w); const fname = words.slice(0, lnameStart).join(' '); const lname = words.slice(lnameStart).join(' '); name = fname + ' ' + nameFixer.nameFixer(lname); name = name.replace('LI', 'Li').replace('XI', 'Xi'); if (cache[id]) return cache[id]; const pages = await (await fetch('https://www.wikidata.org/w/api.php?' + new URLSearchParams({   action: 'query',    format: 'json',    list: 'search',    srsearch: `haswbstatement:P1146=${id}`,  }))).json; const qid = pages.query.search[0]?.title; if (qid) { const entity = await (await fetch('https://www.wikidata.org/w/api.php?' + new URLSearchParams({     action: 'wbgetentities',      format: 'json',      ids: qid,    }))).json; const sitelinks = entity.entities[qid].sitelinks; const enTitle = sitelinks.enwiki?.title; if (enTitle) { cache[id] = `' : }`;     return cache[id];    }    let enLabel = entity.entities[qid].labels.en?.value ?? name;    const enLabelNoParens = enLabel;    if (await titleExists(enLabel)) enLabel = await getSuffix(enLabel, evt, year);    const otherWikis = Object.keys(sitelinks).filter(key => !key.startsWith('commons') && key.endsWith('wiki'));    if (otherWikis.length) {      const positionals = otherWikis.map(ow => `|${ow.replace('wiki', )}|${sitelinks[ow].title}`).join('');      cache[id] = `${enLabel}${positionals}${enLabel.includes('(') ? ``;     return cache[id]; }   cache[id] = `${enLabel}`; return cache[id]; } if (await titleExists(name)) name = await getSuffix(name, evt, year); cache[id] = `' : }`; return cache[id]; } mark2secs=(mark, isField = false)=>{  const parts = mark.split(':');  let ret;  if (parts.length === 1) ret = +mark;  else if (parts.length === 2) ret = +parts[0] * 60 + +parts[1];  else ret = +parts[0] * 60 * 60 + +parts[1] * 60 + +parts[2];  if (Number.isNaN(ret)) return isField ? -Infinity : Infinity;  return ret; } const COLS = 2; let out = ; const combinedData = Object.values(data).reduce((acc, dayData) => { for (let eventTitle of dayData.data.getCalendarCompetitionResults.eventTitles) { eventTitle = structuredClone(eventTitle); const foundEventTitle = acc.data.getCalendarCompetitionResults.eventTitles.find(et => et.eventTitle === eventTitle.eventTitle); if (foundEventTitle) { const oldEvts = [...eventTitle.events]; for (const evt of oldEvts) { const foundEvt = foundEventTitle.events.find(e2 => e2.event === evt.event); if (foundEvt) foundEvt.races.push(...evt.races); else foundEventTitle.events.push(evt); }   }    else acc.data.getCalendarCompetitionResults.eventTitles.push(eventTitle); } return acc; }, { data: { getCalendarCompetitionResults: { eventTitles: [], competition: Object.values(data)[0].data.getCalendarCompetitionResults.competition } } }); startDate = new Date(combinedData.data.getCalendarCompetitionResults.competition.startDate); startDateOrig = combinedData.data.getCalendarCompetitionResults.competition.startDate; for (const eventTitle of combinedData.data.getCalendarCompetitionResults.eventTitles) { eventTitle.pointsTitle = eventTitle.eventTitle;  if (eventTitle.eventTitle?.startsWith('XC ')) eventTitle.pointsTitle = eventTitle.eventTitle.replace(/XC [0-9\.]+km/, 'XC');  console.log(eventTitle.pointsTitle)  if (!['World Athletics Indoor Tour', 'Indoor Meeting', 'U20 Events', 'Masters Events', 'Diamond Discipline', 'Promotional Events', 'National Events', 'U18 Events', 'Regional Races', 'Additional Events', 'XC', null].includes(eventTitle.pointsTitle)) continue; let evtIdx = -1; let etOutput = `===${eventTitle.eventTitle ?? eventTitle.events.find(evt => evt.event === detailEvt)?.races[0].race}===\n` for (const evt of eventTitle.events) { if (detailEvt && evt.event !== detailEvt) continue; let etHasEvents = true; const isLastEvt = eventTitle.events.indexOf(evt) === eventTitle.events.length - 1; const isField = ['jump', 'throw', 'vault', 'discus', 'put'].some(s => evt.event.toLowerCase.includes(s)); const stages = Object.values(evt.races.reduce((acc, r) => { acc[r.race] ??= []; acc[r.race].push(r); return acc; }, {})); for (const stage of stages) { const isLastStage = stages.indexOf(stage) === stages.length - 1; evtIdx++; const isFinal = stage[0].race === 'Final'; const finalQualIds = isFinal ? [] : (stages.find(st => st[0].race === 'Final') ?? []).flatMap(race => race.results).map(res => res.competitor.urlSlug?.split('-').at(-1).replace(/^0/, '')); // Object.assign({}, [...document.querySelector('.records-table').querySelectorAll('tr')].map(tr => tr.querySelectorAll('td')[2]?.innerText).filter(x => x?.trim)) const hasPts = isFinal && eventTitle.pointsTitle === 'World Athletics Indoor Tour' ? {1: 10, 2: 7, 3: 5, 4: 3} : isFinal && eventTitle.pointsTitle === 'Diamond Discipline' ? {1:8,2:7,3:6,4:5,5:4,6:3,7:2,8:1} : eventTitle.pointsTitle === 'XC' ? {"1":"1240","2":"1220","3":"1200","4":"1180","5":"1160","6":"1145","7":"1130","8":"1120","9":"1110","10":"1100","11":"1090","12":"1080","13":"1070","14":"1060","15":"1055","16":"1050","17":"1045","18":"1040","19":"1035","20":"1030","21":"1025","22":"1020","23":"1015","24":"1010","25":"1005","26":"1000","27":"995","28":"990","29":"985","30":"980","31":"975","32":"970","33":"965","34":"960","35":"955","36":"950","37":"945","38":"940","39":"935","40":"930","41":"927","42":"924","43":"921","44":"918","45":"915","46":"912","47":"909","48":"906","49":"903","50":"900","51":"898","52":"896","53":"894","54":"892","55":"890","56":"888","57":"886","58":"884","59":"882","60":"880","61":"879","62":"878","63":"877","64":"876","65":"875","66":"874","67":"873","68":"872","69":"871","70":"870","71":"869","72":"868","73":"887","74":"866","75":"865","76":"864","77":"863","78":"862","79":"861","80":"860"} : null; const isMulti = stage.length > 1; if (evtIdx % COLS === 0) etOutput += '\n'; etOutput += `\n`; const unfilteredResults = stage.flatMap(race => race.results.map(res => ({...res, raceNumber: race.raceNumber}))).map((r, idx, arr) => ({...r, bestWindLegal: !r.competitor.teamMembers && arr.findIndex(r2 => r2.competitor.urlSlug === r.competitor.urlSlug) !== idx})); const bestWindLegals = unfilteredResults.filter(r => r.bestWindLegal); let results = unfilteredResults.filter(r => !r.bestWindLegal) if (doSort) results = results.sort((a, b) => isField ? mark2secs(b.mark, true) - mark2secs(a.mark, true) : mark2secs(a.mark) - mark2secs(b.mark)); const hasWindCol = results.some(res => res.wind); const numTableCols = 4 + hasWindCol + isMulti + hasPts + bdCol; etOutput += `{| class="wikitable mw-datatable sortable" ! Place !! Athlete !!${bdCol ? ' Age !!' : ''}${hideCountry ? '' : ' Country !!'} ${isField ? 'Mark' : 'Time'}${hasWindCol ? ' !! Wind' : ''}${isMulti ? ' !! Heat' : ''}${hasPts ? ' !! Points' : ''}\n`; const getResultRow = async (result, isBestWindLegal = false) => { const pl = doSort ? ((['DNS', 'DNF', 'DQ', 'NM'].includes(result.mark) ?  : results.indexOf(result) + 1) || ) : result.place?.replace('.', ''); const name = result.competitor.name; const dob = new Date(result.competitor.birthDate); const isYearOnly = result.competitor.birthDate?.split(' ').length === 1; const id = result.competitor.urlSlug?.split('-').at(-1).replace(/^0/, ''); return `|-${!isFinal && finalQualIds.includes(id) ? 'bgcolor=#bbf3bb' : ''}\n|align=center| ${{1: '{{Gold1}}', 2: '{{Silver2}}', 3: '{{Bronze3}}'}[isFinal ? pl : 0] ?? pl} || ${id ? await getTitle(id, name, evt.event, dob.getFullYear) : (await Promise.all(result.competitor.teamMembers.map(async tm => await getTitle(tm.id, tm.name, evt.event)))).join(' ')} ${bdCol ? `|| ${result.competitor.birthDate ? `24 ` : }` : }${hideCountry ? '' : `|| `}|| ${isField && (pl || isBestWindLegal) ? `${result.mark} m` : result.mark}${hasWindCol ? ` ||align=right| ${result.wind} m/s` : ''}${isMulti ? ` ||align=center| ${result.raceNumber}` : ''}${hasPts ? ` ||align=center| ${hasPts[pl] ?? }` : }\n`; }   for (const result of results) etOutput += await getResultRow(result); if (bestWindLegals.length) { etOutput += `|-\n!align=center colspan=${numTableCols}| Best wind-legal performances\n`; for (const bwl of bestWindLegals) etOutput += await getResultRow(bwl, true); }   etOutput += '|}\n'; if (evtIdx % COLS === COLS - 1 || (isLastEvt && isLastStage)) etOutput += '\n'; } } if (etOutput.split('\n').length > 2) out += etOutput; } if (!out.endsWith('\n')) out += '\n'; console.log(out); return out; });
 * +${evt.event.replace(' indoor', ) + (isFinal ? (stage.length === 1 && stage[0].wind ? ` (${stage[0].wind} m/s) ` : ) : ` ${stage[0].race}`)}