User:Habst/matchWdWa.js
Appearance
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump. This code will be executed when previewing this page. |
Documentation for this user script can be added at User:Habst/matchWdWa. |
window.wd ??= {};
window.wa ??= {};
window.wp ??= {};
window.redirs ??= {};
const wdEdit = async (qid, prop, val, cmt, refs = [], rank = 'normal') => {
return await (await fetch(`https://www.wikidata.org/w/rest.php/wikibase/v0/entities/items/${qid}/statements`, {
headers: { 'Content-type': 'application/json' },
method: 'POST',
body: JSON.stringify({
statement: {
rank,
property: { id: prop },
value: { type: 'value', content: val },
qualifiers: [], references: refs,
},
tags: [], bot: false,
comment: cmt,
}),
})).json();
};
wikiJsonP = async (props) => {
let wpResult;
window.cb = res => wpResult = res;
const script = Object.assign(document.createElement('script'), {
src: 'https://en.wikipedia.org/w/api.php?' + new URLSearchParams({
action: 'query',
format: 'json',
callback: 'cb',
...props,
})
});
document.body.appendChild(script);
await new Promise(res => script.addEventListener('load', res));
return wpResult;
};
makeWaDate = dt => {
const words = dt.toLocaleString('en-GB', { day: '2-digit', month: 'short', year: 'numeric', timeZone: 'UTC' }).toUpperCase().split(' ');
words.splice(1, 1, words.at(1).slice(0, 3));
return words.join(' ');
}
normalize = s => s.replaceAll('_', ' ').replaceAll('-', ' ').split('(')[0].normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
for (const p of ps['*'][0].a['*']) {
if (p.waid) continue;
const name = normalize(p.title);
console.log(name, p.q);
wd[p.q] ??= await (await fetch(`https://www.wikidata.org/wiki/Special:EntityData/${p.q}.json`)).json();
let dobs = wd[p.q].entities[p.q].claims.P569?.map(c => ({
v: new Date(c.mainsnak.datavalue.value.time.replace('+', '').split('T')[0]),
precision: c.mainsnak.datavalue.value.precision,
})).filter(dob => dob.precision == 11).map(d => makeWaDate(d.v)) ?? [];
redirs[p.q] ??= await wikiJsonP({ prop: 'redirects', generator: 'redirects', formatversion: '2', titles: p.title });
const aliases = wd[p.q].entities[p.q].aliases.en?.map(a => normalize(a.value)) ?? [];
aliases.push(...(redirs[p.q].query?.pages.map(pg => normalize(pg.title)) ?? []));
if (!dobs.length) {
//console.log('no dobs', `https://en.wikipedia.org/wiki/${p.title}`);
wp[p.title] ??= await wikiJsonP({ prop: 'extracts', exchars: '1000', titles: p.title });
const ex = Object.values(wp[p.title].query.pages)[0].extract;
const born = ex.replace(' in ', ')').match(/\(born ([^)]+)\)/)?.[1];
if (born && born.split(' ').length === 3) {
const dt = new Date(born);
if (!p.addedDob) await wdEdit(p.q, 'P569', { time: `+${dt.toLocaleDateString('sv')}T00:00:00Z`, precision: 11, calendarmodel: 'http://www.wikidata.org/entity/Q1985727' }, 'Adding date of birth from enwiki', [{ parts: [{
property: { id: 'P143' },
value: { type: 'value', content: 'Q328' },
}] }], 'preferred');
p.addedDob = true;
dobs = [makeWaDate(dt)];
} else {
console.log('no dob, no born in lede');
continue;
}
}
wa[name] ??= await (await fetch("https://graphql-prod-4607.prod.aws.worldathletics.org/graphql", {
headers: { "x-api-key": "da2-xevhjbj7ybgcnjzrwiad2u6qqq" }, // intentionally public
body: JSON.stringify({
operationName: "SearchCompetitors",
variables: { query: name },
query: `query SearchCompetitors($query: String, $gender: GenderType, $disciplineCode: String, $environment: String, $countryCode: String) {
searchCompetitors(query: $query, gender: $gender, disciplineCode: $disciplineCode, environment: $environment, countryCode: $countryCode) {
aaAthleteId
familyName
givenName
birthDate
disciplines
iaafId
gender
country
urlSlug
__typename
}
}`
}),
method: "POST",
})).json();
let matchingName;
const easternOrder = ['CHN', 'TPE', 'KOR'];
const matches = wa[name].data.searchCompetitors.filter(sc => {
if (!sc.birthDate) return false;
const waNames = [normalize(`${sc.givenName} ${sc.familyName}`)];
if (easternOrder.includes(sc.country)) waNames.push(normalize(`${sc.familyName} ${sc.givenName}`));
const nameMatch = [name, ...aliases].find(n => waNames.includes(n));
if (nameMatch) {
if (dobs.some(dob => sc.birthDate === dob || sc.birthDate === dob.split(' ').at(-1))) {
// allow matching year
matchingName = nameMatch;
return true;
}
}
return false;
});
if (!matches.length) { console.log('no matches'); continue; }
if (matches.length > 1) { console.log('many matches'); continue; }
console.log('*** found match', matches[0].aaAthleteId);
await wdEdit(p.q, 'P1146', matches[0].aaAthleteId, `Adding World Athletics ID with matching name (${matchingName}) and date of birth`);
p.waid = matches[0].aaAthleteId;
}
const todos = ps['*'][0].a['*'].filter(p => !p.waid).map(p => `* [[${p.title}]]`);
console.log(todos.length);
todos.join('\n');