Skip to content

Commit

Permalink
Add [Weblate] badges (#6677)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
3 people authored Jul 2, 2021
1 parent 6aae41e commit 384c57e
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 0 deletions.
58 changes: 58 additions & 0 deletions services/weblate/weblate-component-license.service.js
Original file line number Diff line number Diff line change
@@ -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 changes: 11 additions & 0 deletions services/weblate/weblate-component-license.tester.js
Original file line number Diff line number Diff line change
@@ -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 changes: 74 additions & 0 deletions services/weblate/weblate-entity-count.service.js
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions services/weblate/weblate-entity-count.tester.js
Original file line number Diff line number Diff line change
@@ -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 })
68 changes: 68 additions & 0 deletions services/weblate/weblate-project-translated-percentage.service.js
Original file line number Diff line number Diff line change
@@ -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 })
}
}
13 changes: 13 additions & 0 deletions services/weblate/weblate-project-translated-percentage.tester.js
Original file line number Diff line number Diff line change
@@ -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 changes: 90 additions & 0 deletions services/weblate/weblate-user-statistics.service.js
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions services/weblate/weblate-user-statistics.tester.js
Original file line number Diff line number Diff line change
@@ -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 })

0 comments on commit 384c57e

Please sign in to comment.