-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add web endpoint and cronjob to track category sizes
- Loading branch information
1 parent
c05478d
commit d8b3114
Showing
8 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import {bot, log} from "../botbase"; | ||
import {ApiQueryCategoryInfoParams} from "types-mediawiki/api_params"; | ||
import {ElasticDataStore} from "../elasticsearch"; | ||
import {getKey, normalizeCategory} from "./util"; | ||
|
||
(async function () { | ||
const countStore = new ElasticDataStore('category-counts-enwiki'); | ||
await bot.getTokensAndSiteInfo(); | ||
|
||
const pg = await bot.read('User:SDZeroBot/Category counter'); | ||
const text = pg.revisions[0].content; | ||
|
||
const templates = new bot.Wikitext(text).parseTemplates({ | ||
namePredicate: name => name === 'User:SDZeroBot/Category counter/cat', | ||
}); | ||
|
||
const names = templates.map(t => t.getParam(1).value); | ||
const namesNorm = names.map(name => normalizeCategory(name)).filter(Boolean); | ||
|
||
for await (let json of bot.massQueryGen({ | ||
action: 'query', | ||
titles: namesNorm, | ||
prop: 'categoryinfo' | ||
} as ApiQueryCategoryInfoParams)) { | ||
|
||
for (let pg of json.query.pages) { | ||
if (pg.missing) continue; | ||
|
||
const count = pg.categoryinfo.size; | ||
const date = new bot.Date().format('YYYY-MM-DD', 'utc'); | ||
|
||
const key = getKey(pg.title) | ||
try { | ||
await countStore.append(key, { | ||
[date]: count | ||
}); | ||
} catch (e) { | ||
log(`[E] Failed to insert count of ${count} for ${key}`); | ||
log(e); | ||
} | ||
} | ||
} | ||
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import {bot} from "../botbase"; | ||
import {NS_CATEGORY} from "../namespaces"; | ||
|
||
export function normalizeCategory(name: string) { | ||
if (!name) { | ||
return null; | ||
} | ||
const title = bot.Title.newFromText(name, NS_CATEGORY); | ||
if (title) { | ||
return title.toText(); | ||
} | ||
return null; | ||
} | ||
|
||
/** | ||
* Pass in validated category names only. | ||
*/ | ||
export function getKey(category: string) { | ||
return bot.Title.newFromText(category, NS_CATEGORY).getMain(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import * as express from "express"; | ||
import 'express-async-errors'; | ||
import {ElasticDataStore} from "../elasticsearch"; | ||
import {getKey, normalizeCategory} from "./util"; | ||
|
||
const router = express.Router(); | ||
|
||
const countStore = new ElasticDataStore('category-counts-enwiki'); | ||
|
||
router.get('/raw', async (req, res) => { | ||
let category = normalizeCategory(req.query.category as string); | ||
if (!category) { | ||
return res.status(400).render('webservice/views/oneline', { | ||
text: 'Missing URL parameter "category"' | ||
}) | ||
} | ||
const key = getKey(category); | ||
|
||
if (!await countStore.exists(key)) { // TODO: optimize away this query | ||
return res.status(404).render('webservice/views/oneline', { | ||
text: 'No data found for [[' + category + ']]' | ||
}); | ||
} | ||
|
||
const result = await countStore.get(key); | ||
|
||
return res.status(200).type('json').send(result); | ||
}); | ||
|
||
export default router; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import {Client} from "@elastic/elasticsearch"; | ||
import {onToolforge} from "./utils"; | ||
import {AuthManager} from "./botbase"; | ||
import * as RequestParams from "@elastic/elasticsearch/api/requestParams"; | ||
|
||
export const elastic = new Client({ | ||
node: onToolforge() ? 'http://elasticsearch.svc.tools.eqiad1.wikimedia.cloud:80' : 'http://localhost:9200/', | ||
auth: onToolforge() ? AuthManager.get('elasticsearch') : {}, | ||
}); | ||
|
||
export const cirrus = new Client({ | ||
node: onToolforge() ? 'https://cloudelastic.wikimedia.org:8243/': 'http://localhost:4719', | ||
}); | ||
|
||
export class ElasticDataStore { | ||
private readonly index: string; | ||
constructor(index: string) { | ||
this.index = index; | ||
} | ||
async get(id: string, field?: string) { | ||
const query: RequestParams.Get = { | ||
index: this.index, | ||
id: id | ||
} | ||
if (field) { | ||
query._source = [field]; | ||
} | ||
return elastic.get(query).then(result => result.body._source); | ||
} | ||
async create(id: string, body: any) { | ||
await elastic.index({ | ||
index: this.index, | ||
id: id, | ||
body: body | ||
}); | ||
} | ||
async exists(id: string) { | ||
return elastic.exists({ | ||
index: this.index, | ||
id: id, | ||
}).then(result => result.body); | ||
} | ||
async update(id: string, body: any) { | ||
await elastic.update({ | ||
index: this.index, | ||
id: id, | ||
body: { | ||
doc: body | ||
} | ||
}); | ||
} | ||
async append(id: string, body: any) { | ||
if (!await this.exists(id)) { | ||
await this.create(id, body); | ||
} else { | ||
await this.update(id, body); | ||
} | ||
} | ||
async delete(id: string) { | ||
await elastic.delete({ | ||
index: this.index, | ||
id: id | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters