Add [Weblate] badges (#6677)
* feat: add weblate badges * fix: use color-formatter for translated-percentage * fix: use metric formatter * rm: removed units badge * test: use createservicetester Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com> * fix: refactor weblate badges Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com> Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
This commit is contained in:
58
services/weblate/weblate-component-license.service.js
Normal file
58
services/weblate/weblate-component-license.service.js
Normal file
@@ -0,0 +1,58 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { BaseJsonService } = require('..')
|
||||
const { optionalUrl } = require('../validators')
|
||||
|
||||
const schema = Joi.object({
|
||||
license: Joi.string().required(),
|
||||
}).required()
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
server: optionalUrl.required(),
|
||||
}).required()
|
||||
|
||||
/**
|
||||
* This badge displays the license of a component on a Weblate instance.
|
||||
*/
|
||||
module.exports = class WeblateComponentLicense extends BaseJsonService {
|
||||
static category = 'license'
|
||||
static route = {
|
||||
base: 'weblate/license',
|
||||
pattern: ':project/:component',
|
||||
queryParamSchema,
|
||||
}
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Weblate component license',
|
||||
namedParams: { project: 'godot-engine', component: 'godot' },
|
||||
queryParams: { server: 'https://hosted.weblate.org' },
|
||||
staticPreview: this.render({ license: 'MIT' }),
|
||||
keywords: ['i18n', 'translation', 'internationalization'],
|
||||
},
|
||||
]
|
||||
|
||||
static defaultBadgeData = { label: 'license', color: 'informational' }
|
||||
|
||||
static render({ license }) {
|
||||
return { message: `${license}` }
|
||||
}
|
||||
|
||||
async fetch({ project, component, server }) {
|
||||
return this._requestJson({
|
||||
schema,
|
||||
url: `${server}/api/components/${project}/${component}/`,
|
||||
errorMessages: {
|
||||
403: 'access denied by remote server',
|
||||
404: 'component not found',
|
||||
429: 'rate limited by remote server',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async handle({ project, component }, { server }) {
|
||||
const { license } = await this.fetch({ project, component, server })
|
||||
return this.constructor.render({ license })
|
||||
}
|
||||
}
|
||||
11
services/weblate/weblate-component-license.tester.js
Normal file
11
services/weblate/weblate-component-license.tester.js
Normal file
@@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
const t = (module.exports = require('../tester').createServiceTester())
|
||||
|
||||
t.create('License')
|
||||
.get('/godot-engine/godot.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'license', message: 'MIT' })
|
||||
|
||||
t.create("Component Doesn't Exist")
|
||||
.get('/fake-project/fake-component.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'license', message: 'component not found' })
|
||||
74
services/weblate/weblate-entity-count.service.js
Normal file
74
services/weblate/weblate-entity-count.service.js
Normal file
@@ -0,0 +1,74 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const camelcase = require('camelcase')
|
||||
const { BaseJsonService } = require('..')
|
||||
const { nonNegativeInteger, optionalUrl } = require('../validators')
|
||||
const { metric } = require('../text-formatters')
|
||||
|
||||
const schema = Joi.object({
|
||||
count: nonNegativeInteger,
|
||||
}).required()
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
server: optionalUrl.required(),
|
||||
}).required()
|
||||
|
||||
class WeblateEntityCountBase extends BaseJsonService {
|
||||
static category = 'other'
|
||||
|
||||
static buildRoute(entityName) {
|
||||
return {
|
||||
base: 'weblate',
|
||||
pattern: entityName,
|
||||
queryParamSchema,
|
||||
}
|
||||
}
|
||||
|
||||
static defaultBadgeData = { color: 'informational' }
|
||||
|
||||
async fetch({ entityName, server }) {
|
||||
return this._requestJson({
|
||||
schema,
|
||||
url: `${server}/api/${entityName}/`,
|
||||
errorMessages: {
|
||||
403: 'access denied by remote server',
|
||||
429: 'rate limited by remote server',
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function WeblateEntityCountFactory({ entityName, exampleValue }) {
|
||||
return class WeblateEntityCountService extends WeblateEntityCountBase {
|
||||
static name = camelcase(`Weblate ${entityName}`, { pascalCase: true })
|
||||
static route = this.buildRoute(entityName)
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: `Weblate ${entityName}`,
|
||||
namedParams: {},
|
||||
queryParams: { server: 'https://hosted.weblate.org' },
|
||||
staticPreview: this.render({ count: exampleValue }),
|
||||
keywords: ['i18n', 'internationalization'],
|
||||
},
|
||||
]
|
||||
|
||||
static render({ count }) {
|
||||
return { label: entityName, message: metric(count) }
|
||||
}
|
||||
|
||||
async handle(routeParams, { server }) {
|
||||
const { count } = await this.fetch({ entityName, server })
|
||||
return this.constructor.render({ count })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const entityCounts = [
|
||||
{ entityName: 'components', exampleValue: 2799 },
|
||||
{ entityName: 'projects', exampleValue: 533 },
|
||||
{ entityName: 'users', exampleValue: 33058 },
|
||||
].map(WeblateEntityCountFactory)
|
||||
|
||||
module.exports = [...entityCounts]
|
||||
22
services/weblate/weblate-entity-count.tester.js
Normal file
22
services/weblate/weblate-entity-count.tester.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const { ServiceTester } = require('../tester')
|
||||
const { isMetric } = require('../test-validators')
|
||||
|
||||
const t = (module.exports = new ServiceTester({
|
||||
id: 'WeblateEntity',
|
||||
title: 'Weblate Entity',
|
||||
pathPrefix: '/weblate',
|
||||
}))
|
||||
|
||||
t.create('Components')
|
||||
.get('/components.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'components', message: isMetric })
|
||||
|
||||
t.create('Projects')
|
||||
.get('/projects.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'projects', message: isMetric })
|
||||
|
||||
t.create('Users')
|
||||
.get('/users.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'users', message: isMetric })
|
||||
@@ -0,0 +1,68 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { BaseJsonService } = require('..')
|
||||
const { optionalUrl } = require('../validators')
|
||||
const { colorScale } = require('../color-formatters')
|
||||
|
||||
const schema = Joi.object({
|
||||
translated_percent: Joi.number().required(),
|
||||
}).required()
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
server: optionalUrl.required(),
|
||||
}).required()
|
||||
|
||||
/**
|
||||
* This badge displays the percentage of strings translated on a project on a
|
||||
* Weblate instance.
|
||||
*/
|
||||
module.exports = class WeblateProjectTranslatedPercentage extends (
|
||||
BaseJsonService
|
||||
) {
|
||||
static category = 'other'
|
||||
static route = { base: 'weblate', pattern: ':project', queryParamSchema }
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'Weblate project translated',
|
||||
namedParams: { project: 'godot-engine' },
|
||||
queryParams: { server: 'https://hosted.weblate.org' },
|
||||
staticPreview: this.render({ translatedPercent: 20.5 }),
|
||||
keywords: ['i18n', 'translation', 'internationalization'],
|
||||
},
|
||||
]
|
||||
|
||||
static defaultBadgeData = { label: 'translated' }
|
||||
|
||||
/**
|
||||
* Takes a percentage and maps it to a message and color.
|
||||
*
|
||||
* The colors are determined based on how Weblate does it internally.
|
||||
* {@link https://github.com/WeblateOrg/weblate/blob/main/weblate/trans/widgets.py Weblate on GitHub}
|
||||
*
|
||||
* @param {*} translatedPercent The percentage of translations translated.
|
||||
* @returns {object} Format for the badge.
|
||||
*/
|
||||
static render({ translatedPercent }) {
|
||||
const color = colorScale([75, 90])(translatedPercent)
|
||||
return { message: `${translatedPercent.toFixed(0)}%`, color }
|
||||
}
|
||||
|
||||
async fetch({ project, server }) {
|
||||
return this._requestJson({
|
||||
schema,
|
||||
url: `${server}/api/projects/${project}/statistics/`,
|
||||
errorMessages: {
|
||||
403: 'access denied by remote server',
|
||||
404: 'project not found',
|
||||
429: 'rate limited by remote server',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async handle({ project }, { server }) {
|
||||
const { translated_percent } = await this.fetch({ project, server })
|
||||
return this.constructor.render({ translatedPercent: translated_percent })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const t = (module.exports = require('../tester').createServiceTester())
|
||||
|
||||
const { isPercentage } = require('../test-validators')
|
||||
|
||||
t.create('License')
|
||||
.get('/godot-engine.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'translated', message: isPercentage })
|
||||
|
||||
t.create('Not Valid')
|
||||
.get('/fake-project.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'translated', message: 'project not found' })
|
||||
90
services/weblate/weblate-user-statistics.service.js
Normal file
90
services/weblate/weblate-user-statistics.service.js
Normal file
@@ -0,0 +1,90 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const camelcase = require('camelcase')
|
||||
const { BaseJsonService } = require('..')
|
||||
const { nonNegativeInteger, optionalUrl } = require('../validators')
|
||||
const { metric } = require('../text-formatters')
|
||||
|
||||
const schema = Joi.object({
|
||||
translated: nonNegativeInteger,
|
||||
suggested: nonNegativeInteger,
|
||||
uploaded: nonNegativeInteger,
|
||||
commented: nonNegativeInteger,
|
||||
languages: nonNegativeInteger,
|
||||
}).required()
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
server: optionalUrl.required(),
|
||||
}).required()
|
||||
|
||||
class WeblateUserStatisticBase extends BaseJsonService {
|
||||
static category = 'other'
|
||||
|
||||
static buildRoute(statistic) {
|
||||
return {
|
||||
base: 'weblate/user',
|
||||
pattern: `:user/${statistic}`,
|
||||
queryParamSchema,
|
||||
}
|
||||
}
|
||||
|
||||
static defaultBadgeData = { color: 'informational' }
|
||||
|
||||
async fetch({ user, server }) {
|
||||
return this._requestJson({
|
||||
schema,
|
||||
url: `${server}/api/users/${user}/statistics/`,
|
||||
errorMessages: {
|
||||
403: 'access denied by remote server',
|
||||
404: 'user not found',
|
||||
429: 'rate limited by remote server',
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function WeblateUserStatisticFactory({
|
||||
statisticName,
|
||||
property,
|
||||
exampleValue,
|
||||
}) {
|
||||
return class WeblateUserStatistic extends WeblateUserStatisticBase {
|
||||
static name = camelcase(`Weblate user ${statisticName}`, {
|
||||
pascalCase: true,
|
||||
})
|
||||
|
||||
static route = this.buildRoute(statisticName)
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: `Weblate user ${statisticName}`,
|
||||
namedParams: { user: 'nijel' },
|
||||
queryParams: { server: 'https://hosted.weblate.org' },
|
||||
staticPreview: this.render({ count: exampleValue }),
|
||||
keywords: ['i18n', 'internationalization'],
|
||||
},
|
||||
]
|
||||
|
||||
static render({ count }) {
|
||||
return { label: statisticName, message: metric(count) }
|
||||
}
|
||||
|
||||
async handle({ user }, { server }) {
|
||||
const data = await this.fetch({ user, server })
|
||||
return this.constructor.render({ count: data[property] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const userStatistics = [
|
||||
{
|
||||
statisticName: 'translations',
|
||||
property: 'translated',
|
||||
exampleValue: 30585,
|
||||
},
|
||||
{ statisticName: 'suggestions', property: 'suggested', exampleValue: 7 },
|
||||
{ statisticName: 'languages', property: 'languages', exampleValue: 1 },
|
||||
].map(WeblateUserStatisticFactory)
|
||||
|
||||
module.exports = [...userStatistics]
|
||||
22
services/weblate/weblate-user-statistics.tester.js
Normal file
22
services/weblate/weblate-user-statistics.tester.js
Normal file
@@ -0,0 +1,22 @@
|
||||
'use strict'
|
||||
|
||||
const { ServiceTester } = require('../tester')
|
||||
const { isMetric } = require('../test-validators')
|
||||
|
||||
const t = (module.exports = new ServiceTester({
|
||||
id: 'WeblateUserStatistic',
|
||||
title: 'Weblate User Statistic',
|
||||
pathPrefix: '/weblate',
|
||||
}))
|
||||
|
||||
t.create('Translations')
|
||||
.get('/user/nijel/translations.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'translations', message: isMetric })
|
||||
|
||||
t.create('Suggestions')
|
||||
.get('/user/nijel/suggestions.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'suggestions', message: isMetric })
|
||||
|
||||
t.create('Languages')
|
||||
.get('/user/nijel/languages.json?server=https://hosted.weblate.org')
|
||||
.expectBadge({ label: 'languages', message: isMetric })
|
||||
Reference in New Issue
Block a user