-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
1 parent
6aae41e
commit 384c57e
Showing
8 changed files
with
358 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,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 }) | ||
} | ||
} |
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,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' }) |
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,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] |
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,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
68
services/weblate/weblate-project-translated-percentage.service.js
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,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
13
services/weblate/weblate-project-translated-percentage.tester.js
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,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' }) |
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,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] |
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,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 }) |