diff --git a/src/database/models/ClassificationRoot.js b/src/database/models/ClassificationRoot.js new file mode 100644 index 0000000000000000000000000000000000000000..fe18ca26a420073d2381f9e8a980562d64f5a224 --- /dev/null +++ b/src/database/models/ClassificationRoot.js @@ -0,0 +1,64 @@ +const BaseModel = require('./BaseModel') +const timestamps = require('./properties/timestamps') + +class ClassificationRoot extends BaseModel { + static associate(models) { + this.belongsTo(models.Upload, { foreignKey: 'upload_id' }) + } + + toJSON() { + return { + metric_id: this.metric_id, + upload_id: this.upload_id, + image_id: this.image_id, + url: this.url, + status: this.status, + } + } + +} + +module.exports = (sequelize, DataTypes) => { + ClassificationRoot.init(Object.assign( + { + metric_id: { + type: DataTypes.UUID, + primaryKey: true, + defaultValue: DataTypes.UUIDV4, + validate: { + isUUID: 4, + }, + }, + upload_id: { + type: DataTypes.UUID, + validate: { + isUUID: 4, + }, + }, + image_id: { + type: DataTypes.UUID, + validate: { + isUUID: 4, + }, + }, + url: { + type: DataTypes.TEXT, + }, + status: { + type: DataTypes.TEXT, + }, + meta: { + type: DataTypes.JSONB, + }, + }, + timestamps(DataTypes), + ), { + sequelize, + paranoid: true, + tableName: 'classification_roots', + }) + + return ClassificationRoot +} + +module.exports.Model = ClassificationRoot diff --git a/src/http/controllers/api/v2/classifications.js b/src/http/controllers/api/v2/classifications.js new file mode 100644 index 0000000000000000000000000000000000000000..00f5d559e188691111c455f9e23e6a035d0683de --- /dev/null +++ b/src/http/controllers/api/v2/classifications.js @@ -0,0 +1,71 @@ +const publicStatus = new Set(['accepted']) +const privateStatus = new Set(['pending', 'rejected']) + +const { Sequelize, ClassificationRoot } = require('database/models') +const NotFoundError = require("core/errors/NotFoundError"); +const UnauthorizedError = require("core/errors/UnauthorizedError"); +const InputValidationError = require("core/errors/InputValidationError"); + +exports.listRoots = async ctx => { + const { status = '', limit = 25, after = null } = ctx.query + + const requestedStatus = status.split(',') + .map(s => s.trim()) + .filter(s => publicStatus.has(s) || privateStatus.has(s)) + + const query = { + order: ['metric_id', 'created_at'], + where: { + status: { + [Sequelize.Op.in]: requestedStatus, + } + }, + limit, + } + + const count = await ClassificationRoot.count(query) + + if (after) { + query.where.metric_id = { + [Sequelize.Op.gt]: after, + } + } + + const roots = await ClassificationRoot.findAll(query) + + ctx.body = { + roots, + meta: { + total: count, + } + } +} + +exports.putRootStatus = async ctx => { + const user = await ctx.services['core.auth'].getUser() + + if (!user) { + throw new UnauthorizedError() + } + + if (ctx.models.classification == null) { + throw new NotFoundError('Classification Root') + } + + const { status } = ctx.request.body + const { classification } = ctx.models + + if (!publicStatus.has(status) && !privateStatus.has(status)) { + throw new InputValidationError(['status']) + } + + classification.status = status + classification.meta = { + approved_by: user.id, + } + await classification.save() + + ctx.body = { + root: classification, + } +} \ No newline at end of file diff --git a/src/http/params/classification.js b/src/http/params/classification.js new file mode 100644 index 0000000000000000000000000000000000000000..23362fe0f1b472e75b82432c30a43ca96fb322d0 --- /dev/null +++ b/src/http/params/classification.js @@ -0,0 +1,7 @@ +const { ClassificationRoot } = require('database/models') + +module.exports = async (id, ctx, next) => { + ctx.models = ctx.models ?? {} + ctx.models.classification = await ClassificationRoot.findByPk(id) + return await next() +} diff --git a/src/http/routers/routes_v2.js b/src/http/routers/routes_v2.js index 45691f1663a664f4481155ab33d3614e2eec492e..3a1e040f7ac4adcd73fe6b3ec11d6746ffbdd35a 100644 --- a/src/http/routers/routes_v2.js +++ b/src/http/routers/routes_v2.js @@ -54,6 +54,7 @@ router.put('/uploads/:upload_id/:property', noop) router.param('survey', param('survey')) router.param('excerpt', param('survey_excerpt')) +router.param('classification', param('classification')) router.get('/surveys', controller('api/v2/surveys', 'list')) router.get('/excerpts', controller('api/v2/surveys', 'listExcerpts')) @@ -65,6 +66,9 @@ if (config('app.dev')) { router.post('/surveys/factory', safemode, controller('api/v2/factories', 'survey')) } +router.get('/classifications/roots', controller('api/v2/classifications', 'listRoots')) +router.put('/classifications/roots/:classification/status', controller('api/v2/classifications', 'putRootStatus')) + router.post('/an/ev', safemode, controller('api/analytics', 'track')) module.exports = router