bring back the [PyPI] downloads badges (#2131)

This commit is contained in:
chris48s
2018-10-06 16:38:41 +01:00
committed by GitHub
parent 819202bcf3
commit dea6df0ded
2 changed files with 115 additions and 29 deletions

View File

@@ -1,12 +1,100 @@
'use strict'
const deprecatedService = require('../deprecated-service')
const PypiBase = require('./pypi-base')
const Joi = require('joi')
const BaseJsonService = require('../base-json')
const { downloadCount } = require('../../lib/color-formatters')
const { metric } = require('../../lib/text-formatters')
const { nonNegativeInteger } = require('../validators')
// https://github.com/badges/shields/issues/716
module.exports = ['pypi/dm', 'pypi/dw', 'pypi/dd'].map(base =>
deprecatedService({
category: 'downloads',
url: PypiBase.buildUrl(base),
})
)
const pypiStatsSchema = Joi.object({
data: Joi.object({
last_day: nonNegativeInteger,
last_week: nonNegativeInteger,
last_month: nonNegativeInteger,
}),
}).required()
const periodMap = {
dd: {
api_field: 'last_day',
suffix: '/day',
},
dw: {
api_field: 'last_week',
suffix: '/week',
},
dm: {
api_field: 'last_month',
suffix: '/month',
},
}
// this badge uses PyPI Stats instead of the PyPI API
// so it doesn't extend PypiBase
module.exports = class PypiDownloads extends BaseJsonService {
async fetch({ pkg }) {
const url = `https://pypistats.org/api/packages/${pkg.toLowerCase()}/recent`
return this._requestJson({
url,
schema: pypiStatsSchema,
errorMessages: { 404: 'package not found' },
})
}
static render({ period, downloads }) {
return {
message: `${metric(downloads)}${periodMap[period].suffix}`,
color: downloadCount(downloads),
}
}
async handle({ period, pkg }) {
const json = await this.fetch({ pkg })
return this.constructor.render({
period,
downloads: json.data[periodMap[period].api_field],
})
}
static get defaultBadgeData() {
return { label: 'downloads' }
}
static get category() {
return 'downloads'
}
static get url() {
return {
base: 'pypi',
format: '(dd|dw|dm)/(.+)',
capture: ['period', 'pkg'],
}
}
static get examples() {
return [
{
title: 'PyPI - Downloads',
exampleUrl: 'dd/Django',
urlPattern: 'dd/:package',
staticExample: this.render({ period: 'dd', downloads: 14000 }),
keywords: ['python'],
},
{
title: 'PyPI - Downloads',
exampleUrl: 'dw/Django',
urlPattern: 'dw/:package',
staticExample: this.render({ period: 'dw', downloads: 250000 }),
keywords: ['python'],
},
{
title: 'PyPI - Downloads',
exampleUrl: 'dm/Django',
urlPattern: 'dm/:package',
staticExample: this.render({ period: 'dm', downloads: 1070100 }),
keywords: ['python'],
},
]
}
}

View File

@@ -2,7 +2,7 @@
const Joi = require('joi')
const ServiceTester = require('../service-tester')
const { isSemver } = require('../test-validators')
const { isMetricOverTimePeriod, isSemver } = require('../test-validators')
const isPsycopg2Version = Joi.string().regex(/^v([0-9][.]?)+$/)
@@ -15,37 +15,35 @@ const isPipeSeparatedDjangoVersions = isPipeSeparatedPythonVersions
const t = new ServiceTester({ id: 'pypi', title: 'PyPi badges' })
module.exports = t
/*
tests for downloads endpoints
// tests for downloads endpoints
Note:
Download statistics are no longer available from pypi
it is exptected that the download badges all show
'no longer available'
*/
t.create('daily downloads (expected failure)')
t.create('daily downloads (valid)')
.get('/dd/djangorestframework.json')
.expectJSON({ name: 'downloads', value: 'no longer available' })
.expectJSONTypes({ name: 'downloads', value: isMetricOverTimePeriod })
t.create('weekly downloads (expected failure)')
t.create('weekly downloads (valid)')
.get('/dw/djangorestframework.json')
.expectJSON({ name: 'downloads', value: 'no longer available' })
.expectJSONTypes({ name: 'downloads', value: isMetricOverTimePeriod })
t.create('monthly downloads (expected failure)')
t.create('monthly downloads (valid)')
.get('/dm/djangorestframework.json')
.expectJSON({ name: 'downloads', value: 'no longer available' })
.expectJSONTypes({ name: 'downloads', value: isMetricOverTimePeriod })
t.create('daily downloads (invalid)')
t.create('downloads (mixed-case package name)')
.get('/dd/DjangoRestFramework.json')
.expectJSONTypes({ name: 'downloads', value: isMetricOverTimePeriod })
t.create('daily downloads (not found)')
.get('/dd/not-a-package.json')
.expectJSON({ name: 'downloads', value: 'no longer available' })
.expectJSON({ name: 'downloads', value: 'package not found' })
t.create('weekly downloads (invalid)')
t.create('weekly downloads (not found)')
.get('/dw/not-a-package.json')
.expectJSON({ name: 'downloads', value: 'no longer available' })
.expectJSON({ name: 'downloads', value: 'package not found' })
t.create('monthly downloads (invalid)')
t.create('monthly downloads (not found)')
.get('/dm/not-a-package.json')
.expectJSON({ name: 'downloads', value: 'no longer available' })
.expectJSON({ name: 'downloads', value: 'package not found' })
/*
tests for version endpoint