Skip to content

Commit

Permalink
Add back queryAuthors() method
Browse files Browse the repository at this point in the history
Now using the wikiwho.wmflabs.org endpoint.
  • Loading branch information
siddharthvp committed Mar 22, 2023
1 parent c58edc0 commit 9f2d895
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 2 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Only breaking changes, deprecations and the like are documented in this change l

#### 2.0.0

- mwn#queryAuthors() and `queryAuthors()` on page objects are removed. They relied on the WikiWho API which is now defunct.
- mwn#queryAuthors() which had been deprecated has now been removed. Instead, please use `queryAuthors()` method on page objects.

#### 0.11.0

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Mwn works with both JavaScript and TypeScript. It is created with a design philo

This library provides TypeScript type definitions for all its functions, as well as for MediaWiki API request objects (MW core + several extensions). API responses are also typed for the common operations.

In addition to the MediaWiki Action API, methods are provided to talk to the Wikimedia Pageviews API, the ORES API, and WikiWho API.

This library uses mocha and chai for tests, and has [extensive test coverage](https://coveralls.io/github/siddharthvp/mwn?branch=master). Testing is automated using a CI workflow on GitHub Actions.

To install, run `npm install mwn`.
Expand Down
103 changes: 102 additions & 1 deletion src/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,13 @@ export interface MwnPage extends MwnTitle {
* @param options
*/
pageViews(options?: PageViewOptions): Promise<PageViewData[]>;

/**
* Query the top contributors to the article using the WikiWho API.
* This API has a throttling of 2000 requests a day.
* Supported for EN, DE, ES, EU, TR Wikipedias only
* @see https://wikiwho.wmflabs.org/
*/
queryAuthors(): Promise<AuthorshipData>;
edit(transform: (rev: { content: string; timestamp: string }) => string | ApiEditPageParams): Promise<any>;
save(text: string, summary?: string, options?: ApiEditPageParams): Promise<any>;
newSection(header: string, message: string, additionalParams?: ApiEditPageParams): Promise<any>;
Expand Down Expand Up @@ -549,6 +555,91 @@ export default function (bot: mwn): MwnPageStatic {
});
}

/** @inheritDoc */
async queryAuthors(): Promise<AuthorshipData> {
let langcodematch = bot.options.apiUrl.match(/([^/]*?)\.wikipedia\.org/);
if (!langcodematch || !langcodematch[1]) {
throw new Error('WikiWho API is not supported for bot API URL. Re-check.');
}

let json;
try {
json = await bot
.rawRequest({
url: `https://wikiwho.wmflabs.org/${
langcodematch[1]
}/api/v1.0.0-beta/latest_rev_content/${encodeURIComponent(this.toString())}/?editor=true`,
headers: {
'User-Agent': bot.options.userAgent,
},
})
.then((response) => response.data);
} catch (err) {
throw new Error(err && err.response && err.response.data && err.response.data.Error);
}

const tokens = Object.values(json.revisions[0])[0].tokens;

let data: AuthorshipData = {
totalBytes: 0,
users: [],
};
let userdata: {
[editor: string]: {
name?: string;
bytes: number;
percent?: number;
};
} = {};

for (let token of tokens) {
data.totalBytes += token.str.length;
let editor = token['editor'];
if (!userdata[editor]) {
userdata[editor] = { bytes: 0 };
}
userdata[editor].bytes += token.str.length;
if (editor.startsWith('0|')) {
// IP
userdata[editor].name = editor.slice(2);
}
}

Object.entries(userdata).map(([userid, { bytes }]) => {
userdata[userid].percent = bytes / data.totalBytes;
if (userdata[userid].percent < 0.02) {
delete userdata[userid];
}
});

await bot
.request({
action: 'query',
list: 'users',
ususerids: Object.keys(userdata).filter((us) => !us.startsWith('0|')), // don't lookup IPs
})
.then((json) => {
json.query.users.forEach((us: any) => {
userdata[us.userid].name = us.name;
});
});

data.users = Object.entries(userdata)
.map(([userid, { bytes, name, percent }]) => {
return {
id: Number(userid),
name: name,
bytes: bytes,
percent: percent,
};
})
.sort((a, b) => {
return a.bytes < b.bytes ? 1 : -1;
});

return data;
}

/**** Post operations *****/
// Defined in bot.js

Expand Down Expand Up @@ -600,3 +691,13 @@ export interface PageViewData {
agent: string;
views: number;
}

export interface AuthorshipData {
totalBytes: number;
users: Array<{
id: number;
name: string;
bytes: number;
percent: number;
}>;
}
13 changes: 13 additions & 0 deletions tests/suppl.bot.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,17 @@ describe('supplementary functions', function () {
expect(views[0]).to.be.an('object').with.property('timestamp').that.equals('2021010100');
expect(views[1]).to.be.an('object').with.property('timestamp').that.equals('2021020100');
});

it('wikiwho', async function () {
this.timeout(10000);
await bot.getSiteInfo();
let page = new bot.page('Dairy in India');
let data = await page.queryAuthors();
expect(data).to.be.an('object').with.property('totalBytes').that.is.a('number');
expect(data).to.have.property('users').that.is.instanceOf(Array).of.length.greaterThan(1);
expect(data.users[0]).to.be.an('object').with.property('id').that.is.a('number');
expect(data.users[0]).to.have.property('name').that.is.a('string');
expect(data.users[0]).to.have.property('percent').that.is.a('number');
expect(data.users[0]).to.have.property('bytes').that.is.a('number');
});
});
13 changes: 13 additions & 0 deletions website/docs/11-integration-with-other-apis.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ const pageViewData = await page.pageViews({

The [PageViewOptions](https://mwn.toolforge.org/docs/api/interfaces/pageviewoptions.html) argument is optional. Return type is Promise<<a href="https://mwn.toolforge.org/docs/api/interfaces/pageviewdata.html">PageViewData</a>[]>.

### WikiWho

See <https://wikiwho.wmflabs.org/>

Fetch the list of top contributors to an article. Available for limited number of Wikipedias.

```js
const page = new bot.page('Lorem ipsum');
const contributorData = await page.queryAuthors();
```

Return type is Promise<<a href="https://mwn.toolforge.org/docs/api/interfaces/authorshipdata.html">AuthorshipData</a>>.

### EventStreams

Methods for [EventStreams](https://wikitech.wikimedia.org/wiki/Event_Platform/EventStreams) access have been removed in v2. Consider using the dedicated library <https://www.npmjs.com/package/wikimedia-streams> instead.

0 comments on commit 9f2d895

Please sign in to comment.