User:Habst/WorldAthletics2Medalists.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
window.data ??= {}
for (const day of [1,2])
window.data[day]??=await (await fetch("https://wpgiegzkbrhj5mlsdxnipboepm.appsync-api.eu-west-1.amazonaws.com/graphql", {
  "headers": {
    "x-api-key": "da2-juounigq4vhkvg5ac47mezxqge" // note API key is intentionally public
  },
  "body": JSON.stringify({
  "operationName": "getCalendarCompetitionResults",
  "variables": {
    "competitionId": 7176907,
    "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();
window.cache??={};
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(pre+'https://en.wikipedia.org/wiki/' + name.replace('|', ''));
  return enLabelTitleMatch.status === 200;
}
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] = `[[${enTitle}${enTitle.includes('(') ? '|' : ''}]]`;
      return cache[id];
    }
    let enLabel = entity.entities[qid].labels.en?.value ?? name;
    const enLabelNoParens = enLabel;
    if (await titleExists(enLabel)) enLabel += ' (athlete)'; // todo awb job?
    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] = `{{ill|${enLabel}${positionals}${enLabel.includes('(') ? `|lt=${enLabelNoParens}` : ''}}}`;
      return cache[id];
    }
    cache[id] = `{{ill|${enLabel}|wd=${qid}|s=1${enLabel.includes('(') ? `|lt=${enLabelNoParens}` : ''}}}`;
    return cache[id];
  }
  if (await titleExists(name)) {
    name += ' (athlete)|';
    if (await titleExists(name)) {
      const el = evt.toLowerCase();
      const parens = el.includes('mH') ? 'hurdler' : el.includes('high jump') ? 'high jumper' : el.includes('long jump') ? 'long jumper' : 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' : 'runner';
      name = name.replace('(athlete)', `(${parens})`);
      if (await titleExists(name)) name = name.replace(`(${parens})`, `(${parens}, born ${year})`);
    }
  }
  cache[id] = `[[${name}]]`;
  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 medalRows = [];
for (const dayData of Object.values(data))
for (const eventTitle of dayData.data.getCalendarCompetitionResults?.eventTitles ?? []) {
  if (![null].includes(eventTitle.eventTitle)) continue;
for (const evt of eventTitle.events) {
  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;
    const isFinal = stage[0].race === 'Final';
    if (!isFinal) continue;
    const isMulti = stage.length > 1;
    let medalRow = `|-\n| ${evt.event.replace(' indoor', '')}\n`;
    const results = stage.flatMap(race => race.results.map(res => ({...res, raceNumber: race.raceNumber}))).sort((a, b) => isField ? mark2secs(b.mark, true) - mark2secs(a.mark, true) : mark2secs(a.mark) - mark2secs(b.mark)).filter((r, i, arr) => r.competitor.teamMembers?.length || arr.findIndex(r2 => r.competitor.urlSlug === r2.competitor.urlSlug) === i);
    for (const result of results.slice(0, 3)) {
      const pl = ['DNS', 'DNF', 'DQ', 'NM'].includes(result.mark) ? '' : results.indexOf(result) + 1;
      const name = result.competitor.name;
      const dob = new Date(result.competitor.birthDate);
      const id = result.competitor.urlSlug?.split('-').at(-1).replace(/^0/, '');
      medalRow += `| ${id ? `{{flagmedalist|${await getTitle(id, name, evt.event, dob.getFullYear())}|${result.nationality}}}` : `{{flagmedalist|${result.competitor.name}|${result.nationality}}}<br>${(await Promise.all(result.competitor.teamMembers.map(async tm => await getTitle(tm.id, tm.name)))).join('<br>')}`} || ${result.mark + (isField && pl ? ' m' : '')}\n`;
    }
    medalRows.push(medalRow);
  }
}
}
let out = '';
for (const gen of [`Men's `, `Women's `]) {
  out += `===${gen.replace(`'s `, '')}===\n{| {{MedalistTable|type=Event|columns=2}}\n`;
  out += medalRows.filter(row => row.includes(gen)).map(row => row.replace(gen, '')).sort((a, b) => {
    const evtOrder = ['100m', '200m', '400m', '800m', '1500m', '5000m', '10,000m', 'Marathon', '110mH', '100mH', '400mH', '3000mSC'].reverse();
    const bScore = evtOrder.findIndex(evt => b.includes(evt) && !b.includes('x' + evt));
    const aScore = evtOrder.findIndex(evt => a.includes(evt) && !a.includes('x' + evt));
    if (bScore === -1 && aScore === -1) return a.localeCompare(b);
    return bScore - aScore;
  }).join('');
  out += '|}\n';
}