diff --git a/.eslintrc.json b/.eslintrc.json index 14152dfcb9ae24e77005322efbbfbe811504ae58..bac0de40f23bdc06babf20e5c715f05b3f8b6c41 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,6 +7,8 @@ "import" ], "rules": { + "jsx-a11y/heading-has-content": [ "off" ], + "jsx-a11y/anchor-has-content": [ "off" ], "indent": [ "error", 4 ], "no-unused-vars": [ "error", { "args": "none" }], "no-param-reassign": [ "off" ] diff --git a/.gitignore b/.gitignore index 979717eb93b624b88c6749fccd981b0ccb7b2e8d..08baf221d197e203575471053f219672305703c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.vscode .DS_Store .idea lib-cov @@ -20,3 +21,4 @@ build/* config.json docs/ +.vscode/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 04cdb1f9f46441a3617636eea51bf6403f1e2ab9..2cfd1adfd30e95fcc86afa8f389450e666b68f85 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,6 +22,3 @@ run_tests: - gulp test tags: - node - cache: - paths: - - node_modules/ diff --git a/CHANGELOG.md b/CHANGELOG.md index a8ebb1348286218dd9ad68d64fcd0bfd0cf58a52..6afe076fb4769bfbb6258a289769c58c8d98103e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## 1.0.0 - 2018-03-14 +### Added +- `ReqQueryFields` middleware added to substitute parseParams and do SQL building automagically with the URL params +- Upgrade to database v2 +- Added user model +- Passport.js added for user authentication +- Added routes to create and authenticate a user +- Added simulation model +- Added routes to save and retrieve simulations in `/simulation` +- Add middleware to convert ids to names + +### Changed +- Query middleware throws a 404 when the result is empty +- Change filters and dimensions names! No more `_id`. **Breaks compability** +- config.json.example now has values for development, test and production environments +- Min Node version is 6.8.1 +- Cache is now defined per route +- Errors from MonetDB are now returned by the query_exec and query middlewares +- Tests are now in different files + +### Removed +- Parse Params middleware removed + ## 0.1.0 - 2016-10-10 ### Added **Database** diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e649c4e017254a3cdc008cbcc2597a4da3fe740..39149f5870bd6f98d5644bedae5dbfe23038692a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,6 +17,8 @@ Bugs are reported and tracked at [simcaq/SCRUM](https://gitlab.c3sl.ufpr.br/simc * **development**: default branch * **issue/??**: issue branch - a branch created to solve a issue * **feature_????**: feature branch - a branch created to add a feature +* **hotfix_????**: hotfix branch - a branch created to fix a problem or error +* **release_vM.m.f**: release branch - a branch created to merge with master and set a release. The verion follows the [semantic versioning](http://semver.org) ## Styleguide diff --git a/config.json.example b/config.json.example index 136ee928b34e9554c008f7125400286e6ff2ec33..e3d309200d160d47b17dbe57f23dee11cdbccd23 100644 --- a/config.json.example +++ b/config.json.example @@ -1,21 +1,139 @@ { - "port": 3000, - "ip": "127.0.0.1", - "debug" : false, - "monetdb": { - "host": "simcaqdb1", - "port": 50000, - "dbname": "simcaq_dev", - "user": "monetdb", - "password":"monetdb", - "nrConnections": "16" + "development": + { + "port": 3000, + "ip": "127.0.0.1", + "debug" : true, + "monetdb": { + "host": "simcaqdb3.c3sl.ufpr.br", + "port": 50000, + "dbname": "simcaq_dev4", + "user": "monetdb", + "password":"monetdb", + "nrConnections": "4" + }, + "cdn" : { + "url": "http://simcaqdb3.c3sl.ufpr.br:3000", + "download": "https://simcaqdev.c3sl.ufpr.br/download/" + }, + "mongodb" : { + "uri": "mongodb://localhost/dev_users" + }, + "monq": { + "uri": "mongodb://localhost/dev_monq" + }, + "default": { + "api": { + "version" : "v1" + }, + "lde": { + "url": "http://ldedev.c3sl.ufpr.br/#" + }, + "simcaq": { + "url": "http://simcaqdev.c3sl.ufpr.br/#" + } + }, + "email": { + "port": 25, + "host": "mx.c3sl.ufpr.br", + "secure": false, + "ignoreTLS": true, + "from": "\"Laboratório de Dados Educacionais\" <lde@c3sl.ufpr.br>" + }, + "security": { + "tokenLife": 3600 + } }, - "mongodb" : { - "uri": "mongodb://localhost/users" + "test": + { + "port": 4000, + "ip": "127.0.0.1", + "debug" : true, + "monetdb": { + "host": "simcaqdb3.c3sl.ufpr.br", + "port": 50000, + "dbname": "simcaq_dev4", + "user": "monetdb", + "password":"monetdb", + "nrConnections": "4" + }, + "cdn" : { + "url": "http://simcaqdb3.c3sl.ufpr.br:3000", + "download": "https://simcaqdev.c3sl.ufpr.br/download/" + }, + "mongodb" : { + "uri": "mongodb://localhost/test_users", + "secret": "SimCAQC3SL" + }, + "monq": { + "uri": "mongodb://localhost/test_monq" + }, + "default": { + "api": { + "version" : "v1" + }, + "lde": { + "url": "http://ldedev.c3sl.ufpr.br/#" + }, + "simcaq": { + "url": "http://simcaqdev.c3sl.ufpr.br/#" + } + }, + "email": { + "port": 25, + "host": "mx.c3sl.ufpr.br", + "secure": false, + "ignoreTLS": true, + "from": "\"Laboratório de Dados Educacionais\" <lde@c3sl.ufpr.br>" + }, + "security": { + "tokenLife": 3600 + } }, - "default": { - "api": { - "version" : "v1" + "production": + { + "port": 6000, + "ip": "127.0.0.1", + "debug" : false, + "monetdb": { + "host": "simcaqdb3.c3sl.ufpr.br", + "port": 50000, + "dbname": "simcaq_dev4", + "user": "monetdb", + "password":"monetdb", + "nrConnections": "4" + }, + "cdn" : { + "url": "http://simcaqdb3.c3sl.ufpr.br:7000", + "download": "https://simcaq.c3sl.ufpr.br/download/" + }, + "mongodb" : { + "uri": "mongodb://localhost/users", + "secret": "SimCAQC3SL" + }, + "monq": { + "uri": "mongodb://localhost/monq" + }, + "default": { + "api": { + "version" : "v1" + }, + "lde": { + "url": "http://lde.c3sl.ufpr.br/#" + }, + "simcaq": { + "url": "http://simcaq.c3sl.ufpr.br/#" + } + }, + "email": { + "port": 25, + "host": "mx.c3sl.ufpr.br", + "secure": false, + "ignoreTLS": true, + "from": "\"Laboratório de Dados Educacionais\" <lde@c3sl.ufpr.br>" + }, + "security": { + "tokenLife": 3600 } } } diff --git a/gulpfile.babel.js b/gulpfile.babel.js index b2758b0a6abe55329d30fb9658e0547e5fcb150d..11e9416f87c8528c84d0bbb64f5312c8455215b4 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -47,16 +47,17 @@ gulp.task('compile', ['lint'], () => { .pipe(babel()) // compile only modified files // .pipe(cache.cache()) // cache compiled files .pipe(gulp.dest('build')); // move compiled files to build directory +}); +gulp.task('build', ['compile'], () => { + var filesToCopy = [ 'config.json', 'package.json' ]; // copy configuration file to build directory - gulp.src('config.json') + gulp.src(filesToCopy) .pipe(gulp.dest('build')); createLogDir(); }); -gulp.task('build', ['compile']); - gulp.task('docco', () => { gulp.src('./src/**/*.js') .pipe(docco()) @@ -73,12 +74,17 @@ gulp.task('pre-test', () => { gulp.task('test', ['pre-test'], () => { process.chdir('build'); - gulp.src('test/test.js', {read: false}) - .pipe(mocha()) + gulp.src(['test/**/*.js'], {read: false}) + .pipe(mocha({timeout: 60000})) .pipe(istanbul.writeReports()) .pipe(istanbul.enforceThresholds({ thresholds: { - global: 80 + global: { + statements: 80, + branches: 70, + lines: 80, + functions: 80 + } } })) .on('error', () => { diff --git a/package.json b/package.json index acc29cd2e3d6ff9a605fde13cfaa6774243b6307..65e8d4c96f3372c0348376b054012ae247d64202 100644 --- a/package.json +++ b/package.json @@ -4,30 +4,46 @@ "author": "C3SL", "description": "Simulador custo aluno-qualidade", "private": true, + "engines": { + "node": ">= 6.8.1" + }, + "engineStrict": true, "scripts": { "start": "cd build && forever start server.js || node server.js", "test": "cd build && mocha" }, "dependencies": { - "apicache": "0.0.14", + "agenda": "^0.9.1", + "apicache": "0.7.0", + "bcrypt-nodejs": "0.0.3", "body-parser": "^1.13.1", "chai": "^3.5.0", "chai-http": "^3.0.0", - "compression": "^1.6.2", + "chalk": "^1.1.3", "cookie-parser": "^1.3.5", "cors": "^2.7.1", "csv-express": "^1.1.0", - "debug": "~2.0.x", + "debug": "~2.3.x", "dirty-chai": "^1.2.2", "express": "^4.13.0", - "faker": "^2.1.5", "forever": "^0.15.2", - "js2xmlparser": "^1.0.0", + "js2xmlparser": "^2.0.2", + "jsonexport": "^2.0.9", + "jwt-simple": "^0.5.0", + "lodash": "^4.17.2", "method-override": "^2.3.3", - "mocha": "^2.5.3", + "mocha": "^3.1.2", "monetdb-pool": "0.0.8", "mongoose": "^4.6.0", - "nconf": "^0.6.x", + "nconf": "^0.8.x", + "node-uuid": "^1.4.8", + "nodemailer": "^4.0.1", + "nodemailer-html-to-text": "^2.1.0", + "oauth2orize": "^1.8.1", + "passport": "^0.3.2", + "passport-http-bearer": "^1.0.1", + "passport-oauth2-client-password": "^0.1.2", + "request": "^2.81.0", "squel": "^5.4.2", "winston": "^2.2.0" }, @@ -41,20 +57,20 @@ "browserify": "^13.1.0", "chai-xml": "^0.3.1", "docdash": "^0.4.0", - "eslint": "^3.3.1", - "eslint-config-airbnb": "^10.0.1", - "eslint-plugin-import": "^1.13.0", - "eslint-plugin-jsx-a11y": "^2.1.0", - "eslint-plugin-react": "^6.1.1", + "eslint": "^3.7.1", + "eslint-config-airbnb": "^13.0.0", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-jsx-a11y": "^2.2.3", + "eslint-plugin-react": "^6.4.0", "gulp": "^3.9.1", "gulp-babel": "^6.1.2", "gulp-cli": "^1.2.2", "gulp-docco": "0.0.4", "gulp-eslint": "^3.0.1", "gulp-file-cache": "0.0.1", - "gulp-function": "^1.3.6", + "gulp-function": "^2.2.0", "gulp-istanbul": "^1.1.1", - "gulp-jsdoc3": "^0.3.0", + "gulp-jsdoc3": "^1.0.1", "gulp-mocha": "^3.0.1", "gulp-nodemon": "^2.1.0", "gulp-plumber": "^1.1.0", diff --git a/src/libs/app.js b/src/libs/app.js index f43abfb9732b4793d161c51efaf9b7dc857a674c..6f464ead79b585ef439b825587413177bdc51223 100644 --- a/src/libs/app.js +++ b/src/libs/app.js @@ -3,52 +3,84 @@ const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const methodOverride = require('method-override'); const cors = require('cors'); -const compression = require('compression'); const squel = require('squel'); const libs = `${process.cwd()}/libs`; const log = require(`${libs}/log`)(module); +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; const config = require(`${libs}/config`); -const cache = require('apicache').options({ debug: config.get('debug') }).middleware; +const cache = require('apicache').options({ debug: config.debug }).middleware; const app = express(); -const api = require(`${libs}/routes/api`); +const api = require('./routes/api'); + +const passport = require('passport'); const mongoose = require(`${libs}/db/mongoose`); const db = mongoose(); -app.use(bodyParser.json()); -app.use(bodyParser.urlencoded({ extended: false })); +require(`${libs}/middlewares/passport`); + +app.use(bodyParser.json({limit: '50mb'})); +app.use(bodyParser.urlencoded({ extended: true, limit: '50mb' })); app.use(cookieParser()); // Enable Cross-Origin Resource Sharing (CORS) app.use(cors()); app.use(methodOverride()); -// Enable cache for 1 day -app.use(cache('1 day')); -// Enable maximum compression -app.use(compression(9)); // Middleware tha adds the squel object to req app.use((req, res, next) => { - req.sql = squel.select(); + req.resetSql = () => { + req.sql = squel.select(); + // HACK to know wich table we are using + req.sql.oldFrom = req.sql.from; + req.sql.from = (name, alias = null) => { + req.sql.oldFrom(name, alias || null); + req.sql.tableFrom = name; + return req.sql; + }; + }; + req.resetSql(); + + req.sql.oldClone = req.sql.clone; + req.sql.clone = () => { + let cloned = req.sql.oldClone(); + cloned.oldFrom = cloned.from; + cloned.from = (name, alias = null) => { + cloned.oldFrom(name, alias || null); + cloned.tableFrom = name; + return cloned; + }; + return cloned; + }; + next(); +}); +app.use(passport.initialize()); +app.use(passport.session()); + +app.use((req, res, next) => { + res.setHeader('Last-Modified', (new Date()).toUTCString()); next(); }); +// Mounts all API routes under /api/v1 app.use('/api/v1', api); // Catch 404 and forward to error handler app.use((req, res, next) => { res.status(404); - log.debug('%s %d %s', req.method, res.statusCode, req.url); - res.json({ error: 'Not found' }).end(); + log.error('%s %d %s', req.method, res.statusCode, req.url); + res.json({ error: 'Error 404: Page not found' }).end(); }); -// Error handlers +// Express' default error handler app.use((err, req, res, next) => { res.status(err.status || 500); log.error('%s %d %s', req.method, res.statusCode, err.message); + log.error(`Route: ${req.originalUrl}`); + log.error(err); res.json({ error: err.message }).end(); }); diff --git a/src/libs/config.js b/src/libs/config.js index 5aea5a9ca50efd0dac2253dd23690229431236dc..2c07d19e94ba2505205d613a9ac6d5596c388502 100644 --- a/src/libs/config.js +++ b/src/libs/config.js @@ -1,8 +1,5 @@ -const nconf = require('nconf'); +let conf = require(`${process.cwd()}/config.json`); -// Exports the config.json as an object with get functions -nconf.argv() - .env() - .file({ file: `${process.cwd()}/config.json` }); +conf = conf[process.env.NODE_ENV]; -module.exports = nconf; +module.exports = conf; diff --git a/src/libs/convert/admDependency.js b/src/libs/convert/admDependency.js new file mode 100644 index 0000000000000000000000000000000000000000..16017f06634d2d9b69ad1e605c4922c2b29ff243 --- /dev/null +++ b/src/libs/convert/admDependency.js @@ -0,0 +1,14 @@ +module.exports = function admDependency(id) { + switch (id) { + case 1: + return 'Federal'; + case 2: + return 'Estadual'; + case 3: + return 'Municipal'; + case 4: + return 'Privada'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/admDependencyPriv.js b/src/libs/convert/admDependencyPriv.js new file mode 100644 index 0000000000000000000000000000000000000000..62afaa8354d2ebabe30826e66aceeb2a8c036790 --- /dev/null +++ b/src/libs/convert/admDependencyPriv.js @@ -0,0 +1,18 @@ +module.exports = function admDependencyPriv(id) { + switch (id) { + case 1: + return 'Federal'; + case 2: + return 'Estadual'; + case 3: + return 'Municipal'; + case 4: + return 'Privada conveniada'; + case 5: + return 'Privada não conveniada sem fins lucrativos'; + case 6: + return 'Privada não conveniada com fins lucrativos'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/ageRange.js b/src/libs/convert/ageRange.js new file mode 100644 index 0000000000000000000000000000000000000000..b24990a1bd9870c90365d0af9b118c1578b1dd3a --- /dev/null +++ b/src/libs/convert/ageRange.js @@ -0,0 +1,18 @@ +module.exports = function ageRange(id) { + switch (id) { + case 1: + return '0-3'; + case 2: + return '4-5'; + case 3: + return '6-10'; + case 4: + return '11-14'; + case 5: + return '15-17'; + case 6: + return '18-24'; + default: + return 'Não declarada'; + } +}; diff --git a/src/libs/convert/agreement.js b/src/libs/convert/agreement.js new file mode 100644 index 0000000000000000000000000000000000000000..e9c5c774b0af69ed5b47d23e20893d7a953b8a4c --- /dev/null +++ b/src/libs/convert/agreement.js @@ -0,0 +1,12 @@ +module.exports = function agreement(id) { + switch (id) { + case 1: + return 'Municipal'; + case 2: + return 'Estadual'; + case 3: + return 'Estadual e Municipal'; + default: + return 'Não declarada'; + } +}; diff --git a/src/libs/convert/booleanVariable.js b/src/libs/convert/booleanVariable.js new file mode 100644 index 0000000000000000000000000000000000000000..8ed8e007e2597049009072955c46319e32891a1c --- /dev/null +++ b/src/libs/convert/booleanVariable.js @@ -0,0 +1,8 @@ +module.exports = function booleanVariable(id) { + if (id == null) + return 'Não Declarado'; + else if (id == false) + return 'Não'; + else if (id == true) + return 'Sim'; +}; diff --git a/src/libs/convert/citySize.js b/src/libs/convert/citySize.js new file mode 100644 index 0000000000000000000000000000000000000000..819eb150e8a91e9546cf6935ce7b000b63242789 --- /dev/null +++ b/src/libs/convert/citySize.js @@ -0,0 +1,20 @@ +module.exports = function citySize(id) { + switch (id) { + case 1: + return 'até 5000'; + case 2: + return '5001 - 10000'; + case 3: + return '10001 - 20000'; + case 4: + return '20001 - 50000'; + case 5: + return '50001 - 100000'; + case 6: + return '100001 - 500000'; + case 7: + return 'mais que 500000'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/contractType.js b/src/libs/convert/contractType.js new file mode 100644 index 0000000000000000000000000000000000000000..6c9167ce6e3758feb8de1d20c7a3b228be1e4f17 --- /dev/null +++ b/src/libs/convert/contractType.js @@ -0,0 +1,14 @@ +module.exports = function idhmLevel(id) { + switch (id) { + case 1: + return 'Concursado/Efetivo/Estavél'; + case 2: + return 'Contrato temporário'; + case 3: + return 'Contrato terceirizado'; + case 4: + return 'Contrato CLT'; + default: + return 'Não classificado'; + } +}; diff --git a/src/libs/convert/educationLevel.js b/src/libs/convert/educationLevel.js new file mode 100644 index 0000000000000000000000000000000000000000..e01c8be0f2a5ea682281bca1d95e396d9b2d9068 --- /dev/null +++ b/src/libs/convert/educationLevel.js @@ -0,0 +1,108 @@ +module.exports = function educationLevel(id) { + switch (id) { + case 1: + return 'Educação Infantil - Creche'; + case 2: + return 'Educação Infantil - Pré-Escola'; + case 3: + return 'Educação Infantil - Unificada'; + case 4: + return 'Ensino Fundamental de 8 anos - 1ª Série'; + case 5: + return 'Ensino Fundamental de 8 anos - 2ª Série'; + case 6: + return 'Ensino Fundamental de 8 anos - 3ª Série'; + case 7: + return 'Ensino Fundamental de 8 anos - 4ª Série'; + case 8: + return 'Ensino Fundamental de 8 anos - 5ª Série'; + case 9: + return 'Ensino Fundamental de 8 anos - 6ª Série'; + case 10: + return 'Ensino Fundamental de 8 anos - 7ª Série'; + case 11: + return 'Ensino Fundamental de 8 anos - 8ª Série'; + case 12: + return 'Ensino Fundamental de 8 anos - Multi'; + case 13: + return 'Ensino Fundamental de 8 anos - Correção de Fluxo'; + case 14: + return 'Ensino Fundamental de 9 anos - 1º Ano'; + case 15: + return 'Ensino Fundamental de 9 anos - 2º Ano'; + case 16: + return 'Ensino Fundamental de 9 anos - 3º Ano'; + case 17: + return 'Ensino Fundamental de 9 anos - 4º Ano'; + case 18: + return 'Ensino Fundamental de 9 anos - 5º Ano'; + case 19: + return 'Ensino Fundamental de 9 anos - 6º Ano'; + case 20: + return 'Ensino Fundamental de 9 anos - 7º Ano'; + case 21: + return 'Ensino Fundamental de 9 anos - 8º Ano'; + case 22: + return 'Ensino Fundamental de 9 anos - Multi'; + case 23: + return 'Ensino Fundamental de 9 anos - Correção de Fluxo'; + case 24: + return 'Ensino Fundamental de 8 e 9 anos - Multi 8 e 9 anos'; + case 25: + return 'Ensino Médio - 1ª Série'; + case 26: + return 'Ensino Médio - 2ª Série'; + case 27: + return 'Ensino Médio - 3ª Série'; + case 28: + return 'Ensino Médio - 4ª Série'; + case 29: + return 'Ensino Médio - Não Seriada 30 - Curso Técnico Integrado (Ensino Médio Integrado) 1ª Série'; + case 30: + return 'Curso Técnico Integrado (Ensino Médio Integrado) 1ª Série'; + case 31: + return 'Curso Técnico Integrado (Ensino Médio Integrado) 2ª Série'; + case 32: + return 'Curso Técnico Integrado (Ensino Médio Integrado) 3ª Série'; + case 33: + return 'Curso Técnico Integrado (Ensino Médio Integrado) 4ª Série'; + case 34: + return 'Curso Técnico Integrado (Ensino Médio Integrado) Não Seriada'; + case 35: + return 'Ensino Médio - Normal/Magistério 1ª Série'; + case 36: + return 'Ensino Médio - Normal/Magistério 2ª Série'; + case 37: + return 'Ensino Médio - Normal/Magistério 3ª Série'; + case 38: + return 'Ensino Médio - Normal/Magistério 4ª Série'; + case 39: + return 'Curso Técnico - Concomitante'; + case 40: + return 'Curso Técnico - Subsequente'; + case 41: + return 'Ensino Fundamental de 9 anos - 9º Ano'; + case 56: + return 'Educação Infantil e Ensino Fundamental (8 e 9 anos) Multietapa'; + case 64: + return 'Curso Técnico Misto (Concomitante e Subsequente)'; + case 65: + return 'EJA - Ensino Fundamental - Projovem Urbano'; + case 67: + return 'Curso FIC integrado na modalidade EJA - Nível Médio'; + case 68: + return 'Curso FIC Concomitante'; + case 69: + return 'EJA - Ensino Fundamental - Anos Iniciais EJA - Ensino Fundamental - Anos Iniciais'; + case 70: + return 'EJA - Ensino Fundamental - Anos Finais'; + case 71: + return 'EJA - Ensino Médio'; + case 72: + return 'EJA - Ensino Fundamental - Anos Iniciais e Anos Finais'; + case 73: + return 'Curso FIC integrado na modalidade EJA - Nível Fundamental (EJA integrada à Educação Profissional de Nível Fundamental)'; + case 74: + return 'Curso Técnico Integrado na Modalidade EJA (EJA integrada à Educação Profissional de Nível Médio)'; + } +}; diff --git a/src/libs/convert/educationLevelBasic.js b/src/libs/convert/educationLevelBasic.js new file mode 100644 index 0000000000000000000000000000000000000000..d667d09d57aa5cbe0b38d36f5f9a5aaece124fd8 --- /dev/null +++ b/src/libs/convert/educationLevelBasic.js @@ -0,0 +1,16 @@ +module.exports = function educationLevelBasic(id) { + switch (id) { + case 1: + return 'Creche'; + case 2: + return 'Pré-Escola'; + case 4: + return 'Ensino Fundamental - anos iniciais'; + case 5: + return 'Ensino Fundamental - anos finais'; + case 6: + return 'Ensino Médio'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/educationLevelMod.js b/src/libs/convert/educationLevelMod.js new file mode 100644 index 0000000000000000000000000000000000000000..f3797ee93a25085c444654923fe80d816ff671b3 --- /dev/null +++ b/src/libs/convert/educationLevelMod.js @@ -0,0 +1,26 @@ +module.exports = function educationLevelMod(id) { + switch (id) { + case 1: + return 'Creche'; + case 2: + return 'Pré-Escola'; + case 3: + return 'Educação Infantil Unificada'; + case 4: + return 'Ensino Fundamental - anos iniciais'; + case 5: + return 'Ensino Fundamental - anos finais'; + case 6: + return 'Ensino Médio'; + case 7: + return 'Turmas multisseriadas e multieatapas'; + case 8: + return 'EJA - Ensino Fundamental'; + case 9: + return 'EJA - Ensino Médio'; + case 10: + return 'Educação Profissional'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/educationLevelShort.js b/src/libs/convert/educationLevelShort.js new file mode 100644 index 0000000000000000000000000000000000000000..ee97e956453c30af7ae7d37d60a6e2708b5dab2c --- /dev/null +++ b/src/libs/convert/educationLevelShort.js @@ -0,0 +1,20 @@ +module.exports = function educationLevelShort(id) { + switch (id) { + case 1: + return 'Creche'; + case 2: + return 'Pré-Escola'; + case 3: + return 'Ensino Fundamental - anos iniciais'; + case 4: + return 'Ensino Fundamental - anos finais'; + case 5: + return 'Ensino Médio'; + case 6: + return 'EJA'; + case 7: + return 'EE exclusiva'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/educationType.js b/src/libs/convert/educationType.js new file mode 100644 index 0000000000000000000000000000000000000000..0fffb330c453b0c6f71cbdd9e25078f1ac6e246b --- /dev/null +++ b/src/libs/convert/educationType.js @@ -0,0 +1,22 @@ +module.exports = function educationType(id) { + switch (id) { + case 1: + return 'Ensino Fundamental'; + case 2: + return 'Ensino Médio'; + case 3: + return 'Médio Normal ou Magistério'; + case 4: + return 'Superior'; + case 5: + return 'Superior com licenciatura'; + case 6: + return 'Especialização'; + case 7: + return 'Mestrado'; + case 8: + return 'Doutorado'; + default: + return 'Não definido'; + } +}; diff --git a/src/libs/convert/ethnicGroup.js b/src/libs/convert/ethnicGroup.js new file mode 100644 index 0000000000000000000000000000000000000000..dfa51c810879cdd3655b73939766c2eae29333dd --- /dev/null +++ b/src/libs/convert/ethnicGroup.js @@ -0,0 +1,18 @@ +module.exports = function ethnicGroup(id) { + switch (id) { + case 0: + return 'Não declarada'; + case 1: + return 'Branca'; + case 2: + return 'Preta'; + case 3: + return 'Parda'; + case 4: + return 'Amarela'; + case 5: + return 'Indígena'; + default: + return 'Não declarada'; + } +}; diff --git a/src/libs/convert/ethnicGroupPnad.js b/src/libs/convert/ethnicGroupPnad.js new file mode 100644 index 0000000000000000000000000000000000000000..93428cd4440f4559dda7ff10c429c135e263960a --- /dev/null +++ b/src/libs/convert/ethnicGroupPnad.js @@ -0,0 +1,12 @@ +module.exports = function ethnicGroupPnad(id) { + switch (id) { + case 0: + return 'Indígena'; + case 1: + return 'Branca e amarela'; + case 2: + return 'Preta e parda'; + default: + return 'Sem declaração'; + } +}; diff --git a/src/libs/convert/extremesHouseholdIncome.js b/src/libs/convert/extremesHouseholdIncome.js new file mode 100644 index 0000000000000000000000000000000000000000..67904c4ff2a4418f4dcb1f3516c8552bdabdcac7 --- /dev/null +++ b/src/libs/convert/extremesHouseholdIncome.js @@ -0,0 +1,10 @@ +module.exports = function extremesHouseholdIncome(id) { + switch (id) { + case 1: + return '10% menores'; + case 2: + return '10% maiores'; + default: + return 'Sem declaração'; + } +}; diff --git a/src/libs/convert/fifthHouseholdIncome.js b/src/libs/convert/fifthHouseholdIncome.js new file mode 100644 index 0000000000000000000000000000000000000000..ec7669a2a6180ce95d64888f1841fe2c017eeaa5 --- /dev/null +++ b/src/libs/convert/fifthHouseholdIncome.js @@ -0,0 +1,16 @@ +module.exports = function fifthHouseholdIncome(id) { + switch (id) { + case 1: + return '20% menores'; + case 2: + return '2o quinto'; + case 3: + return '3o quinto'; + case 4: + return '4o quinto'; + case 5: + return '20% maiores'; + default: + return 'Sem declaração'; + } +}; diff --git a/src/libs/convert/gender.js b/src/libs/convert/gender.js new file mode 100644 index 0000000000000000000000000000000000000000..085eb20e0aa7fa18c2e9c3106fc6c8869b0bd639 --- /dev/null +++ b/src/libs/convert/gender.js @@ -0,0 +1,8 @@ +module.exports = function gender(id) { + switch(id) { + case 1: + return 'Masculino'; + case 2: + return 'Feminino'; + } +}; diff --git a/src/libs/convert/genderPnad.js b/src/libs/convert/genderPnad.js new file mode 100644 index 0000000000000000000000000000000000000000..9d5da51e6e8115d4c7de9baacd14b4e77756c11a --- /dev/null +++ b/src/libs/convert/genderPnad.js @@ -0,0 +1,8 @@ +module.exports = function genderPnad(id) { + switch(id) { + case 2: + return 'Masculino'; + case 4: + return 'Feminino'; + } +}; diff --git a/src/libs/convert/idhmLevel.js b/src/libs/convert/idhmLevel.js new file mode 100644 index 0000000000000000000000000000000000000000..c9f1845c6147e4f59bbbcf7aaecd5934ac5c8017 --- /dev/null +++ b/src/libs/convert/idhmLevel.js @@ -0,0 +1,16 @@ +module.exports = function idhmLevel(id) { + switch (id) { + case 1: + return 'Muito Baixa'; + case 2: + return 'Baixo'; + case 3: + return 'Médio'; + case 4: + return 'Alto'; + case 5: + return 'Muito Alto'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/incomeLevel.js b/src/libs/convert/incomeLevel.js new file mode 100644 index 0000000000000000000000000000000000000000..c39106843027d91d628ec6bdda664507e660aae8 --- /dev/null +++ b/src/libs/convert/incomeLevel.js @@ -0,0 +1,16 @@ +module.exports = function citySize(id) { + switch (id) { + case 1: + return '1º quintil – 20% menores'; + case 2: + return '2º quintil'; + case 3: + return '3º quintil'; + case 4: + return '4º quintil'; + case 5: + return '5º quintil – 20% maiores'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/location.js b/src/libs/convert/location.js new file mode 100644 index 0000000000000000000000000000000000000000..606e06fc46a87bc5f1d1bf9c61aa8ad5ad06a353 --- /dev/null +++ b/src/libs/convert/location.js @@ -0,0 +1,16 @@ +module.exports = function location(id) { + switch (id) { + case 1: + return 'Urbana'; + case 2: + return 'Rural'; + case 3: + return 'Área de assentamento'; + case 4: + return 'Terra indígena'; + case 5: + return 'Área remanescente de quilombos'; + case 6: + return 'Unidade de uso sustentável'; + } +}; diff --git a/src/libs/convert/period.js b/src/libs/convert/period.js new file mode 100644 index 0000000000000000000000000000000000000000..06dbcf588e58ea2739b2e556ceac8a23ba8939d5 --- /dev/null +++ b/src/libs/convert/period.js @@ -0,0 +1,12 @@ +module.exports = function period(id) { + switch(id) { + case 1: + return 'Matutino'; + case 2: + return 'Vespertino'; + case 3: + return 'Noturno'; + default: + return 'Indefinido'; + } +}; diff --git a/src/libs/convert/ruralLocation.js b/src/libs/convert/ruralLocation.js new file mode 100644 index 0000000000000000000000000000000000000000..7fa97d527a5001cf8d8ebf2f0a2fac4fcdd6cfe5 --- /dev/null +++ b/src/libs/convert/ruralLocation.js @@ -0,0 +1,16 @@ +module.exports = function ruralLocation(id) { + switch (id) { + case 1: + return 'Urbana'; + case 2: + return 'Rural'; + case 3: + return 'Rural - Área de assentamento'; + case 4: + return 'Rural - Terra indígena'; + case 5: + return 'Rural - Área remanescente de quilombos'; + case 6: + return 'Rural - Unidade de uso sustentável'; + } +}; diff --git a/src/libs/convert/schoolYear.js b/src/libs/convert/schoolYear.js new file mode 100644 index 0000000000000000000000000000000000000000..f6b677ccd7c9ac39b8439504b0495e5d0e6dcbf0 --- /dev/null +++ b/src/libs/convert/schoolYear.js @@ -0,0 +1,54 @@ +module.exports = function schoolYear(id) { + switch(id) { + case 11: + return 'Creche - Menor de 1 ano'; + case 12: + return 'Creche - 1 ano'; + case 13: + return 'Creche - 2 anos'; + case 14: + return 'Creche - 3 anos'; + case 21: + return 'Pré-escola - 4 anos'; + case 22: + return 'Pré-escola - 5 anos'; + case 31: + return 'Ens. Fundamental - 1º Ano'; + case 32: + return 'Ens. Fundamental - 1ª série/2º ano'; + case 33: + return 'Ens. Fundamental - 2ª série/3º ano'; + case 34: + return 'Ens. Fundamental - 3ª série/4º ano'; + case 35: + return 'Ens. Fundamental - 4ª série/5º Ano'; + case 41: + return 'Ens. Fundamental - 5ª série/6º ano'; + case 42: + return 'Ens. Fundamental - 6ª série/7º ano'; + case 43: + return 'Ens. Fundamental - 7ª série/8º ano'; + case 44: + return 'Ens. Fundamental - 8ª serie/9º ano'; + case 51: + return 'Ens. Médio - 1ª série'; // equivalent to 'EM 1 Série' + case 52: + return 'Ens. Médio - 2ª série'; // equivalent to 'EM 2 Série' + case 53: + return 'Ens. Médio - 3ª série'; // equivalent to 'EM 3 Série' + case 54: + return 'Ens. Médio - 4ª série'; // equivalent to 'EM 4 Série' + case 61: + return 'EJA - anos iniciais do Ens. Fundamental'; + case 62: + return 'EJA - anos finais do Ens. Fundamental'; + case 63: + return 'EJA - Ensino Médio'; + case 64: + return 'EJA semi-presencial'; + case 71: + return 'Educação Profissional'; + default: + return 'Não classificada'; + } +}; diff --git a/src/libs/convert/stateName.js b/src/libs/convert/stateName.js new file mode 100644 index 0000000000000000000000000000000000000000..3fdfdba4cf8ad67cda3b922a4ffd5f43e1e96011 --- /dev/null +++ b/src/libs/convert/stateName.js @@ -0,0 +1,58 @@ +module.exports = function stateName(id) { + switch (id) { + case 11: + return 'Rondônia'; + case 12: + return 'Acre'; + case 13: + return 'Amazonas'; + case 14: + return 'Roraima'; + case 15: + return 'Pará'; + case 16: + return 'Amapá'; + case 17: + return 'Tocantins'; + case 21: + return 'Maranhão'; + case 22: + return'Piauí'; + case 23: + return 'Ceará'; + case 24: + return 'Rio Grande do Norte'; + case 25: + return 'Paraíba'; + case 26: + return 'Pernambuco'; + case 27: + return 'Alagoas'; + case 28: + return 'Sergipe'; + case 29: + return 'Bahia'; + case 31: + return 'Minas Gerais'; + case 32: + return 'Espírito Santo'; + case 33: + return 'Rio de Janeiro '; + case 35: + return 'São Paulo'; + case 41: + return 'Paraná'; + case 42: + return 'Santa Catarina'; + case 43: + return 'Rio Grande do Sul'; + case 50: + return 'Mato Grosso do Sul'; + case 51: + return 'Mato Grosso'; + case 52: + return 'Goiás'; + case 53: + return 'Distrito Federal'; + } +}; diff --git a/src/libs/db/monet.js b/src/libs/db/monet.js index dd7e620adcf5b6a1dbe29358697fa71a3970ea03..d19fb37dfac5ed68cc15c1762a822818dbbc82da 100644 --- a/src/libs/db/monet.js +++ b/src/libs/db/monet.js @@ -6,16 +6,16 @@ const config = require(`${libs}/config`); // Connection options const poolOptions = { - nrConnections: config.get('monetdb:nrConnections'), + nrConnections: config.monetdb.nrConnections, }; // Configuration options const options = { - host: config.get('monetdb:host'), - port: config.get('monetdb:port'), - dbname: config.get('monetdb:dbname'), - user: config.get('monetdb:user'), - password: config.get('monetdb:password'), + host: config.monetdb.host, + port: config.monetdb.port, + dbname: config.monetdb.dbname, + user: config.monetdb.user, + password: config.monetdb.password, }; // Connection singleton diff --git a/src/libs/db/mongoose.js b/src/libs/db/mongoose.js index 30b4d4930c203537335c46b14bfb5e2958ace6bb..f9d2ed8ef59767e262f77fa21e2a93d444139145 100644 --- a/src/libs/db/mongoose.js +++ b/src/libs/db/mongoose.js @@ -6,9 +6,12 @@ const log = require(`${libs}/log`)(module); const mongoose = require('mongoose'); +mongoose.Promise = global.Promise; + module.exports = () => { - const mongoUri = process.env.MONGO_URI || config.get('mongodb:uri'); - log.debug(`Connecting to MongDB on URI ${mongoUri}`); + // Get mongodb URI (ip and port) in config file + const mongoUri = process.env.MONGO_URI || config.mongodb.uri; + log.info(`Connecting to MongoDB on URI ${mongoUri}`); // Connection singleton const db = mongoose.connect(mongoUri); diff --git a/src/libs/db/query_exec.js b/src/libs/db/query_exec.js index 71ae6d9824ac304eb5fd4e4d0e122b16d8d159a2..691117c5672df92ed565c2d4db5e27937b34dfbf 100644 --- a/src/libs/db/query_exec.js +++ b/src/libs/db/query_exec.js @@ -19,27 +19,31 @@ function execSqlQuery(sqlQuery, sqlQueryParams = []) { log.debug(`Executing SQL query '${sqlQuery}' with params '${sqlQueryParams}'`); return new Promise((resolve, reject) => { // Prepare statement - conn.prepare(sqlQuery, true).then( - (dbQuery) => { - // Execute query - dbQuery.exec(sqlQueryParams).then( - // Success - (dbResult) => { - log.debug(`Query result: ${dbResult.data}`); - log.debug(dbResult.data); - resolve(dbResult.data); - }, - // Error - (dbError) => { - log.error(`SQL query execution error: ${dbError.message}`); - reject(new Error(dbError.message)); - } - ); - // Release resources allocated for prepared statement - conn.release(); - } - ); + conn.prepare(sqlQuery, true).then((dbQuery) => { + // Execute query + dbQuery.exec(sqlQueryParams).then((dbResult) => { + // release resources allocated for the prepared statement + dbQuery.release(); + resolve(dbResult.data); + }).catch((queryError) => { + log.error(`SQL query execution error: ${queryError.message}`); + log.error(`SQL query: ${sqlQuery} with params: ${sqlQueryParams}`); + // release resources allocated for the prepared statement + dbQuery.release(); + reject(new Error(queryError.message)); + }); + }).catch((prepError) => { + log.error(`SQL prepared statement error: ${prepError.message}`); + log.error(`SQL query: ${sqlQuery} with params: ${sqlQueryParams}`); + reject(new Error(prepError.message)); + }); }); } -module.exports = execSqlQuery; +function execMultiQuery(querySet = []) { + // Issue all queries concurrently to the database, for every query object in the iterable + // NOTE: Array.map() returns a copy of the original array with each object 'mapped'. + return querySet.map((qry) => { return execSqlQuery(qry.toString()); }); +} + +module.exports = {execSqlQuery, execMultiQuery}; diff --git a/src/libs/log.js b/src/libs/log.js index fbe7b1394464e06611cbcd91d69e33c022846923..ddf0303f8481514b047aa6678894316500d25a96 100644 --- a/src/libs/log.js +++ b/src/libs/log.js @@ -47,7 +47,7 @@ function logger(module) { ], exitOnError: false, }); - if (!config.get('debug')) { + if (!config.debug) { log.remove('debug-log'); } return log; diff --git a/src/libs/middlewares/addMissing.js b/src/libs/middlewares/addMissing.js new file mode 100644 index 0000000000000000000000000000000000000000..2c6ac526651c9d1f362bd831018003135fe64fdb --- /dev/null +++ b/src/libs/middlewares/addMissing.js @@ -0,0 +1,68 @@ +module.exports = function addMissing(rqf){ + return (req, res, next) => { + let dims = Object.keys(req.dims).filter(i => {return i !== 'size';}); + // FIXME: No momento, só funciona para duas dimensões (padrão para o LDE) + if(dims.length != 2) return next(); + if(req.filter.min_year != req.filter.max_year) return next(); + if(req.result.length === 0) return next(); + + let result = req.result; + let dimsValues = {}; + result.forEach((r) => { + dims.forEach((dim) => { + let rqfName = rqf.fieldValues[dim].resultField; + if(typeof dimsValues[rqfName] === 'undefined') { + dimsValues[rqfName] = []; + } + if(dimsValues[rqfName].indexOf(r[rqfName]) === -1) { + dimsValues[rqfName].push(r[rqfName]); + } + }) + }); + + let G = {}; + let rqfName = rqf.fieldValues[dims[0]].resultField; + let rqfName2 = rqf.fieldValues[dims[1]].resultField; + G[rqfName] = {}; + dimsValues[rqfName].forEach((value) => { + let secondDim = {}; + + dimsValues[rqfName2].forEach((dValue) => { + secondDim[dValue] = false; + }); + + G[rqfName][value] = {}; + G[rqfName][value][rqfName2] = secondDim; + }); + + result.forEach((r) => { + let resultDim1 = r[rqfName]; + let resultDim2 = r[rqfName2]; + + G[rqfName][resultDim1][rqfName2][resultDim2] = true; + }); + + Object.keys(G[rqfName]).forEach((dim1Value) => { + Object.keys(G[rqfName][dim1Value][rqfName2]).forEach((dim2Value) => { + let value = G[rqfName][dim1Value][rqfName2][dim2Value]; + if(!value) { + let newEntry = Object.assign({}, result[0], {[rqfName]: parseInt(dim1Value, 10), [rqfName2]: parseInt(dim2Value, 10), total: 0}); + // result.push(newEntry); + let index = 0; + for(let i = 0; i < result.length; ++i) { + let r = result[i]; + index = i; + if(r[rqfName] > newEntry[rqfName]) break; + if(r[rqfName] == newEntry[rqfName] && r[rqfName2] > newEntry[rqfName2]) break; + } + let newResult = [...result.slice(0, index), newEntry, ...result.slice(index, result.length)]; + result = newResult; + } + }); + }); + + req.result = result; + + next(); + }; +}; \ No newline at end of file diff --git a/src/libs/middlewares/checkVersion.js b/src/libs/middlewares/checkVersion.js new file mode 100644 index 0000000000000000000000000000000000000000..c3187adba9081e2a992d09df36e839efc23dfca0 --- /dev/null +++ b/src/libs/middlewares/checkVersion.js @@ -0,0 +1,22 @@ +const curPath = process.cwd(); +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const chalk = require('chalk'); +const packageConf = require(`${curPath}/package.json`); + +module.exports = () => { + // Parse version number from strings such as 'v4.2.0' or `>=4.0.0' + function parseVersionNumber(versionString) { + return parseFloat(versionString.replace(/[^\d\.]/g, '')); + } + // Ensure minimum supported node version is used + const minNodeVersion = parseVersionNumber(packageConf.engines.node); + const currentNodeVersion = parseVersionNumber(process.version); + if (minNodeVersion > currentNodeVersion) { + log.error(chalk.red(`You must upgrade node to >=${minNodeVersion}.x to use simcaq-node!`)); + return false; + } else { + log.info('Node version should work!'); + return true; + } +}; diff --git a/src/libs/middlewares/downloadDatabase.js b/src/libs/middlewares/downloadDatabase.js new file mode 100644 index 0000000000000000000000000000000000000000..c4672ff0bf5d925d7d8d0466a260d3025fbc4e09 --- /dev/null +++ b/src/libs/middlewares/downloadDatabase.js @@ -0,0 +1,88 @@ +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const execute = require(`${libs}/middlewares/query`).execute; + +const request = require(`request`); + +const config = require(`${libs}/config`); + +const Download = require(`${libs}/models/download`); + +module.exports = function download(table, mappingTable) { + return (req, res, next) => { + // First, query the mapping + execute(`SELECT target_name, name FROM ${mappingTable}`, undefined, (err, result) => { + if(err) { + log.error(err.stack); + next(new Error('Request could not be satisfied due to a database error.')); + } else { + let header = ''; + req.sql.from(table); + result.forEach((field) => { + req.sql.field(`CASE ${table}.${field.name} WHEN true THEN 1 WHEN false THEN 0 ELSE ${table}.${field.name} END AS ${field.target_name}`); + // req.sql.field(table + '.' + field.name, field.target_name); + if(header === '') header += field.target_name; + else header = header + ';' + field.target_name; + }); + + let form = { + query: req.sql.toString(), + table: req.sql.tableFrom, + name: req.sql.tableFrom, + username: req.user.name, + email: req.user.email, + header + }; + + request.post(config.cdn.url + '/api/v1/file', {form}, (err, response, body) => { + if(err) { + log.error(err); + return res.json({error: err}); + } + + Download.findOne({query: req.sql.toString()}, (err, download) => { + if(download) { + download.updatedAt = Date.now(); + if(download.userId != req.user._id) { + let dl = new Download({ + userId: req.user._id, + table, + name: req.query.name, + mappingTable, + query: req.sql.toString(), + status: 'Enviando', + expired: false + }); + console.log(dl); + dl.save((err) => { + if(err) log.error(err); + }); + } + } else { + download = new Download({ + userId: req.user._id, + table, + name: req.query.name, + mappingTable, + query: req.sql.toString(), + query: req.sql.toString(), + status: 'Enviando', + expired: false + }); + console.log(download); + } + + download.save((err) => { + if(err) { + log.error(err); + } + res.json({msg: 'Wait for download email', waitForIt: true}); + }); + }); + }); + } + }); + } +}; \ No newline at end of file diff --git a/src/libs/middlewares/email.js b/src/libs/middlewares/email.js new file mode 100644 index 0000000000000000000000000000000000000000..7e0403d423cebd1bd0a8c6c31f57eef5773860f4 --- /dev/null +++ b/src/libs/middlewares/email.js @@ -0,0 +1,37 @@ +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const config = require(`${libs}/config`); +const nodemailer = require('nodemailer'); +const htmlToText = require('nodemailer-html-to-text').htmlToText; + +let transporter = nodemailer.createTransport({ + host: config.email.host, + port: config.email.port, + secure: config.email.secure, + ignoreTLS: config.email.ignoreTLS +}); + +transporter.use('compile', htmlToText()); + +// verify connection configuration +transporter.verify(function(error, success) { + if (error) { + log.error(error); + } else { + log.info('Email server is ready to take our messages'); + } +}); + +const mailOptions = { + from: config.email.from +}; + +module.exports = function send(options, cb) { + let opt = Object.assign({}, mailOptions, options); + transporter.sendMail(opt, (err, info) => { + if(err) { + return cb(err); + } + cb(null, info); + }); +}; \ No newline at end of file diff --git a/src/libs/middlewares/id2str.js b/src/libs/middlewares/id2str.js new file mode 100644 index 0000000000000000000000000000000000000000..a6c4af2ed6a429baaeffc90d2a0ec220ba4eea58 --- /dev/null +++ b/src/libs/middlewares/id2str.js @@ -0,0 +1,128 @@ +const libs = `${process.cwd()}/libs`; +const gender = require(`${libs}/convert/gender`); +const period = require(`${libs}/convert/period`); +const schoolYear = require(`${libs}/convert/schoolYear`); +const admDependency = require(`${libs}/convert/admDependency`); +const admDependencyPriv = require(`${libs}/convert/admDependencyPriv`); +const location = require(`${libs}/convert/location`); +const ruralLocation = require(`${libs}/convert/ruralLocation`); +const ethnicGroup = require(`${libs}/convert/ethnicGroup`); +const agreement = require(`${libs}/convert/agreement`); +const booleanVariable = require(`${libs}/convert/booleanVariable`); +const educationLevel = require(`${libs}/convert/educationLevel`); +const educationLevelMod = require(`${libs}/convert/educationLevelMod`); +const educationLevelShort = require(`${libs}/convert/educationLevelShort`); +const educationType = require(`${libs}/convert/educationType`); +const citySize = require(`${libs}/convert/citySize`); +const incomeLevel = require(`${libs}/convert/incomeLevel`); +const idhmLevel = require(`${libs}/convert/idhmLevel`); +const stateName = require(`${libs}/convert/stateName`); +const contractType = require(`${libs}/convert/contractType`); +const ethnicGroupPnad = require(`${libs}/convert/ethnicGroupPnad`); +const ageRange = require(`${libs}/convert/ageRange`); +const genderPnad = require(`${libs}/convert/genderPnad`); +const fifthHouseholdIncome = require(`${libs}/convert/fifthHouseholdIncome`); +const extremesHouseholdIncome = require(`${libs}/convert/extremesHouseholdIncome`); +const educationLevelBasic = require(`${libs}/convert/educationLevelBasic`); + +const ids = { + gender_id: gender, + period_id: period, + school_year_id: schoolYear, + education_level_id: educationLevel, + education_level_basic_id: educationLevelBasic, + education_level_mod_id: educationLevelMod, + education_level_short_id: educationLevelShort, + adm_dependency_id: admDependency, + adm_dependency_detailed_id: admDependencyPriv, + location_id: location, + rural_location_id: ruralLocation, + location_detailed_id: ruralLocation, + ethnic_group_id: ethnicGroup, + agreement_id: agreement, + integral_time_id: booleanVariable, + government_agreement_id: booleanVariable, + education_day_care_child_id: booleanVariable, + education_preschool_child_id: booleanVariable, + education_begin_elementary_school_id: booleanVariable, + education_end_elementary_school_id: booleanVariable, + education_middle_school_id: booleanVariable, + education_professional_id: booleanVariable, + education_eja_id: booleanVariable, + education_type_id: educationType, + income_level_id: incomeLevel, + city_size_id: citySize, + idhm_level_id: idhmLevel, + state_id: stateName, + contract_type_id: contractType, + ethnic_group_pnad_id: ethnicGroupPnad, + age_range_id: ageRange, + gender_pnad_id: genderPnad, + fifth_household_income_id: fifthHouseholdIncome, + extremes_household_income_id: extremesHouseholdIncome +}; + +function transform(removeId=false) { + return (req, res, next) => { + if(req.result.length <= 0) { + return next(); + } + // Para cada objeto do resultado + req.result.forEach((obj) => { + Object.keys(obj).forEach((key) => { + // Se não há uma função especificada, retorna + if(typeof ids[key] === 'undefined') return; + let id = obj[key]; + obj[key.replace('_id', '_name')] = ids[key](id); + if(removeId) delete obj[key]; + }); + }); + next(); + }; +} + +function multitransform(removeId=false) { + return (req, res, next) => { + Object.keys(req.result[0]).forEach((query) => { + req.result[0][query].forEach((obj) => { + Object.keys(obj).forEach((key) => { + if(typeof ids[key] === 'undefined') return; + let id = obj[key]; + obj[key.replace('_id', '_name')] = ids[key](id); + if(removeId) delete obj[key]; + }); + }) + }); + next(); + } +} + +module.exports = { + transform, + multitransform, + gender, + period, + schoolYear, + educationLevel, + educationLevelBasic, + educationLevelMod, + educationLevelShort, + admDependency, + admDependencyPriv, + location, + ruralLocation, + ethnicGroup, + agreement, + booleanVariable, + educationType, + incomeLevel, + citySize, + idhmLevel, + stateName, + contractType, + ethnicGroupPnad, + ageRange, + genderPnad, + fifthHouseholdIncome, + extremesHouseholdIncome +}; diff --git a/src/libs/middlewares/multiQuery.js b/src/libs/middlewares/multiQuery.js new file mode 100644 index 0000000000000000000000000000000000000000..7bf37c5d9086f3a97405ef2bfbb61553afd0d2bf --- /dev/null +++ b/src/libs/middlewares/multiQuery.js @@ -0,0 +1,16 @@ +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const db = require(`${libs}/db/query_exec`); + +// Middleware that executes multiples queries +function multiQuery(req, res, next) { + Promise.all(db.execMultiQuery(req.querySet)).then((queryResults) => { + req.result = queryResults; + next(); + }).catch((err) => { + log.error(`[SQL query error] ${err}`); + next(err); + }); +} + +module.exports = multiQuery; diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js new file mode 100644 index 0000000000000000000000000000000000000000..cc9a45ede4a3158bc055166202c6d20fb84a6dff --- /dev/null +++ b/src/libs/middlewares/oauth2.js @@ -0,0 +1,121 @@ +const oauth2orize = require('oauth2orize'); +const passport = require('passport'); +const crypto = require('crypto'); + +const libs = `${process.cwd()}/libs`; + +const config = require(`${libs}/config`); +const log = require(`${libs}/log`)(module); + +const db = require(`${libs}/db/mongoose`); +const User = require(`${libs}/models/user`); +const AccessToken = require(`${libs}/models/accessToken`); +const RefreshToken = require(`${libs}/models/refreshToken`); + +// create OAuth 2.0 server +let aserver = oauth2orize.createServer() + +// Generic error handler +let errFn = (cb, err) => { + if (err) { + return cb(err) + } +} + +// Destroys any old tokens and generates a new access and refresh token +let generateTokens = (data, done) => { + // curries in `done` callback so we don't need to pass it + let errorHandler = errFn.bind(undefined, done); + let refreshToken; + let refreshTokenValue; + let token; + let tokenValue; + + RefreshToken.remove(data, errorHandler); + AccessToken.remove(data, errorHandler); + + tokenValue = crypto.randomBytes(32).toString('hex'); + refreshTokenValue = crypto.randomBytes(32).toString('hex'); + + data.token = tokenValue; + token = new AccessToken(data); + + data.token = refreshTokenValue; + refreshToken = new RefreshToken(data); + + refreshToken.save(errorHandler); + + token.save((err) => { + if (err) { + log.error(err); + return done(err); + } + done(null, tokenValue, refreshTokenValue, { + 'expires_in': config.security.tokenLife + }); + }) +}; + +// Exchange username & password for access token. +aserver.exchange(oauth2orize.exchange.password((client, username, password, scope, done) => { + User.findOne({ email: username }, (err, user) => { + if (err) { + return done(err); + } + + if (!user || !user.checkPassword(password)) { + return done(null, false); + } + + var model = { + userId: user._id, + clientId: client._id + }; + log.info(`Gerando token para usuário ${user.name}`); + generateTokens(model, done); + }) + +})); + +// Exchange refreshToken for access token. +aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope, done) =>{ + RefreshToken.findOne({ token: refreshToken, clientId: client._id }, (err, token) => { + if (err) { + return done(err); + } + + if (!token) { + return done(null, false); + } + + User.findById(token.userId, (err, user) => { + if (err) { + log.error(err); + return done(err); + } + if (!user) { + return done(null, false); + } + + var model = { + userId: user._id, + clientId: client._id + }; + + generateTokens(model, done); + }) + }) +})) + +// token endpoint +// +// `token` middleware handles client requests to exchange authorization grants +// for access tokens. Based on the grant type being exchanged, the above +// exchange middleware will be invoked to handle the request. Clients must +// authenticate when making requests to this endpoint. + +exports.token = [ + passport.authenticate(['oauth2-client-password'], { session: false }), + aserver.token(), + aserver.errorHandler() +]; diff --git a/src/libs/middlewares/parseParams.js b/src/libs/middlewares/parseParams.js deleted file mode 100644 index c74b6b491bc445b22cd480c85d9f232494a139de..0000000000000000000000000000000000000000 --- a/src/libs/middlewares/parseParams.js +++ /dev/null @@ -1,66 +0,0 @@ -/** -* ParseParams middleware -* -* EXAMPLE: -* Use it with no parameters to get all the params specified -* app.get('/', parseParams('dims'), function(req, res, next){}) -* -* Use it with an array of accepted values -* app.get('/', parseParams('filter', ['year', 'location']), function(req, res, next){}) -* -* Use it globally -* app.use(parseParams('dims')) -*/ - -const libs = `${process.cwd()}/libs`; - -const log = require(`${libs}/log`)(module); - - // This function returns the intersection of two arrays -function intersect(a, b) { - let t; - if (b.length > a.length) { - t = b; b = a; a = t; - } - return a.filter((e) => b.indexOf(e) !== -1); -} - -function parseParams(queryParam, arr) { - return (req, res, next) => { - req[queryParam] = {}; - if (req.query[queryParam]) { - const params = req.query[queryParam].split(','); - // Temporary object to hold the params and it's values - const obj = {}; - for (const param of params) { - // Get the key and the value - state:41 is key 'state' whith value 41 - const kv = param.split(':'); - // Check if there is a value. If there isn't, assign null - obj[kv[0]] = (typeof kv[1] === 'undefined') ? null : kv[1]; - } - - // If the array exists and is not empty we intersect - if (typeof arr !== 'undefined' && arr.length > 0) { - // Intersect the keys of the obj with the array arr. - // The intersection array is assigned with the keys - const intersection = intersect(arr, Object.keys(obj)); - // This is a bit tricky... - // For each key in the intersection array we get it's value in the obj - // and assign it to the custom attribute in the req obj. - // For example: instersection => ["state"] so - // obj[intersection[i]] (with i=0) is obj["state"], that is 41 - // and req[queryParam]["state"] = 41 - for (let i = 0; i < intersection.length; ++i) { - req[queryParam][intersection[i]] = obj[intersection[i]]; - } - req[queryParam].size = intersection.length; - } else { - req[queryParam] = obj; - req[queryParam].size = Object.keys(obj).length; - } - } - next(); - }; -} - -module.exports = parseParams; diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js new file mode 100644 index 0000000000000000000000000000000000000000..ab895a9604072e3b77a6384ac0c0f9066dcf6753 --- /dev/null +++ b/src/libs/middlewares/passport.js @@ -0,0 +1,67 @@ +const passport = require('passport'); +const ClientPasswordStrategy = require('passport-oauth2-client-password'); +const BearerStrategy = require('passport-http-bearer').Strategy; + +const libs = `${process.cwd()}/libs`; +const config = require(`${libs}/config`); + +const User = require(`${libs}/models/user`); +const Client = require(`${libs}/models/client`); +const AccessToken = require(`${libs}/models/accessToken`); +const RefreshToken = require(`${libs}/models/refreshToken`); +const email = require(`${libs}/middlewares/email`); + +passport.use(new ClientPasswordStrategy( (clientId, clientSecret, done) => { + Client.findOne({ _id: clientId }, (err, client) => { + if (err) { + return done(err); + } + + if (!client) { + return done(null, false); + } + + if (client.clientSecret !== clientSecret) { + return done(null, false); + } + + return done(null, client); + }) + } +)); + +passport.use(new BearerStrategy( (accessToken, done) => { + AccessToken.findOne({ token: accessToken }, (err, token) => { + if (err) { + return done(err); + } + + if (!token) { + return done(null, false); + } + + if( Math.round((Date.now()-token.created)/1000) > config.security.tokenLife) { + AccessToken.remove({ token: accessToken }, (err) => { + if (err) { + return done(err); + } + }); + + return done(null, false, { msg: 'Token expired' }); + } + + User.findById(token.userId, function(err, usuario) { + if (err) { + return done(err); + } + + if (!usuario) { + return done(null, false, { msg: 'Unknown user' }); + } + + var info = { scope: '*' }; + done(null, usuario, info); + }) + }) + } +)); diff --git a/src/libs/middlewares/query.js b/src/libs/middlewares/query.js index a4f20e3ba5bdd31cf7dab85a44121b8e8e456fc0..ae002df2fb13727152497dc9ad61c1371d98d194 100644 --- a/src/libs/middlewares/query.js +++ b/src/libs/middlewares/query.js @@ -1,18 +1,29 @@ const libs = `${process.cwd()}/libs`; const log = require(`${libs}/log`)(module); -const execQuery = require(`${libs}/db/query_exec`); +const db = require(`${libs}/db/query_exec`); // Middleware that executes a query defined by a squel object in req.sql function query(req, res, next) { let sql = req.sql.toParam(); - log.debug(sql); - execQuery(sql.text, sql.values).then((result) => { - log.debug(result); - req.result = result; - next(); + log.info(`Executando query ${req.sql.toString()}`); + execute(sql.text, sql.values, (err, result) => { + if(err) { + log.error(err.stack); + next(new Error('Request could not be satisfied due to a database error.')); + } else { + req.result = result; + next(); + } + }); +} + +function execute(text, values, cb) { + db.execSqlQuery(text, values).then((result) => { + cb(null, result); }, (error) => { - next(error); + log.error(error.stack); + cb(new Error('Request could not be satisfied due to a database error.')); }); } -module.exports = query; +module.exports = {query, execute}; diff --git a/src/libs/middlewares/reqQueryFields.js b/src/libs/middlewares/reqQueryFields.js new file mode 100644 index 0000000000000000000000000000000000000000..5b28567fcf5ded59647c712a2a3dbd17719bab00 --- /dev/null +++ b/src/libs/middlewares/reqQueryFields.js @@ -0,0 +1,335 @@ +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const _ = require('lodash'); + +function parseWhereValue(type, value) { + if(type === 'integer') return parseInt(value, 10); + if(type === 'double') return parseFloat(value); + if(type === 'string') return '%'+value+'%'; + if(type === 'boolean') { + if(value === null || typeof value === 'boolean') { + return value; + } + if(typeof value === 'string') { + if(value.toLowerCase() === 'true' || value.toLowerCase() === '1') { + return true; + } + if(value.toLowerCase() === 'null') { + return null; + } + } + return false; + } +} + +class ReqQueryFields { + constructor(fields = {}, fieldValues = {}) { + // Exemplo de requisição: `/api/v1/enrollments?dims=state,region,location` + + // Parâmetros no campo query da requisição. + // Exemplo de field: + // ```js + // { + // name: 'dims', + // field: true, + // where: false + // } + // ``` + // Array de valores aceitos pelo campo. + // Exemplo de valor: + // ``` + // { + // name: 'location', + // table: 'localizacao', + // tableField: 'descricao' + // resultField: 'location_name', + // where: { + // relation: '=', + // type: 'integer', + // field: 'id_localizacao', + // table: 'turma' + // }, + // join: { + // primary: 'pk_localizacao_id', + // foreign: 'id_localizacao', + // foreignTable: 'turma' + // } + // } + // ``` + this.fields = fields; + this.fieldValues = fieldValues; + } + + addField(field) { + // Parâmetro no campo query da requisição. + // Exemplo de field: + // ``` + // { + // name: 'dims', + // field: true, + // where: false, + // fieldValues: {} + // } + // ``` + if(typeof this.fields[field.name] === 'undefined') { + this.fields[field.name] = field; + } + return this; + } + + addValue(fieldValue) { + // Valor aceito por **todos** os campos. + // Exemplo de valor: + // ``` + // { + // name: 'location', + // table: 'localizacao', + // tableField: 'descricao' + // resultField: 'location_name', + // where: { + // relation: '=', + // type: 'integer', + // field: 'id_localizacao', + // table: 'turma' + // }, + // join: { + // primary: 'pk_localizacao_id', + // foreign: 'id_localizacao', + // foreignTable: 'turma' + // } + // } + // ``` + if(typeof this.fieldValues[fieldValue.name] === 'undefined') { + this.fieldValues[fieldValue.name] = fieldValue; + } + return this; + } + + addValueToField(fieldValue, field) { + // Valor aceito **apenas** pelo campo `field`. + if(typeof this.fields[field] === 'undefined') { + // Se o campo não existe, lança uma exception + throw 'No field with name ' +field+ ' defined'; + } + if(typeof this.fields[field].values === 'undefined') { + this.fields[field].values = {}; + } + if(typeof this.fields[field].values[fieldValue.name] === 'undefined') { + this.fields[field].values[fieldValue.name] = fieldValue; + } + return this; + } + + parse() { + // Faz o parse dos valores que vem na requisição para objetos. + return (req, res, next) => { + // "Foreach" nos campos aceitos + Object.keys(this.fields).map((key, index) => { + let params = []; + // f é o campo + let f = this.fields[key]; + // Unimos os valores parametros globalmente com os aceitos apenas pelo campo + let values = _.merge(this.fieldValues, f.values); + // Fazemos um foreach nos parametros aceitos + Object.keys(values).map((k, i) => { + let value = values[k]; + // Pushamos o parametro + params.push(value.name); + }); + let queryField = f.name; + let arrayOfParams = params; + // Criamos o atributo com o nome do **campo** no objeto `req` (nome do campo é 'filter', 'dims', 'search', etc) + req[queryField] = {}; + if (req.query[queryField]) { + // Se há mais de um parametro no campo, eles estão separados por vírgula. + // Fazemos o split então para separar os valores + const regex = /,(?=[a-z])/; // Pega as vírgulas que não estão nos atributos multivalorados + const params = req.query[queryField].split(regex); + // Objeto temporário para guardar os parametros e seus valores. + const obj = {}; + for (const param of params) { + // O parametro *pode* ter um valor (por exemplo: `state:41`). + // Fazemos o split e temos um array `['state', 41]` + const kv = param.split(':'); + // Checa se há um valor. Se não tem, definimos como true. + if ( (typeof kv[1] === 'undefined')) { + obj[kv[0]] = true; + } else { + try { + obj[kv[0]] = JSON.parse(kv[1]); + } catch(err) { + obj[kv[0]] = kv[1]; + } + } + // `obj` é agora `{kv[0]: kv[1]}` ou `{kv[0]: true}`. + // No exemplo `{'state': 41}` + } + + // Se o array existe e não está vazio fazemos a interseção + if (typeof arrayOfParams !== 'undefined' && arrayOfParams.length > 0) { + // Fazemos a interseção das chaves de `obj` com o array `arrayOfParams`. + // O array resultante são as chaves aceitas pelo campo. + const intersection = _.intersection(arrayOfParams, Object.keys(obj)); + // Isso é um pouco complicado... + // Para cada chave na interseção pegamos seu valor em `obj` + // e atribuímos para o atributo que definimos no objeto `req`. + // Por exemplo: intersection = `['state']` então + // `obj[intersection[i]]` (com i=0) é `obj['state']`, cujo valor é 41. + // Então fazemos `req[queryField]['state'] = 41` + for (let i = 0; i < intersection.length; ++i) { + req[queryField][intersection[i]] = obj[intersection[i]]; + } + req[queryField].size = intersection.length; + } else { + req[queryField] = obj; + req[queryField].size = Object.keys(obj).length; + } + } + }); + next(); + }; + } + + buildQuery(req, sql) { + // "Constrói" o SQL + let hasJoined = {}; + let thisTable = sql.tableFrom; + // Foreach no campos + Object.keys(this.fields).forEach((key) => { + // Campo + let field = this.fields[key]; + // log.debug(field); + // `param` aqui é o atributo no objeto `req` (dims, filter, search, ...) + let param = req[field.name]; + // Fazemos um foreach nos parametros dentro do atributo + Object.keys(param).forEach((k) => { + let values = _.merge(this.fieldValues, field.values); + if(typeof values[k] !== 'undefined') { + // Clonamos para não alterar o original + let value = _.clone(values[k]); + if(value.parseOnly) return; + // Checa se não fizemos o join para este valor e se é necessário fazer + if(!hasJoined[value.table] && typeof value.join !== 'undefined') { + let foreignTable = ''; + if(value.join.foreignTable) foreignTable = value.join.foreignTable+'.'; + if(value.join.foreignTable === '@') foreignTable = thisTable+'.'; + // Fazemos o join + let onClause = ''; + if(Array.isArray(value.join.primary)) { + // Se é um array, montamos a cláusula ON com mais de uma coluna + value.join.primary.forEach((column, index, arr) => { + onClause += foreignTable+value.join.foreign[index]+'='+value.table+'.'+column; + if(index < arr.length-1) { + onClause+=' AND '; + } + }); + } else { + onClause = foreignTable+value.join.foreign+'='+value.table+'.'+value.join.primary; + } + sql.join(value.table, null, onClause); + // Marcamos o join como feito para não ter problemas + hasJoined[value.table] = true; + } + // Se o valor é um campo a ser incluído no SELECT + if(typeof field.field !== 'undefined' && field.field) { + // log.debug('SELECT'); + let table = value.table; + if(table === '@') table = thisTable; + if (Array.isArray(value.resultField)) { + value.tableField.forEach((f, i) => { + sql.field(table+'.'+f, value.resultField[i] || f) + .group(table+'.'+f) + .order(table+'.'+f); + }) + }else{ + sql.field(table+'.'+value.tableField, value.resultField || value.tableField) + .order(table+'.'+value.tableField) + .group(table+'.'+value.tableField); + } + } + // Se o valor é um campo para ser usado no WHERE + if(typeof field.where !== 'undefined' && field.where) { + // log.debug('WHERE'); + // Valor do where + + let whereValue = param[k]; + // log.debug('whereValue'); + // log.debug(whereValue); + // log.debug(`Where value é array? ${Array.isArray(whereValue)}`); + + let tbl = value.where.table || value.table; + if (tbl === '@') tbl = thisTable; + // multiple where + if (Array.isArray(value.where.field)) { + let lower = (value.where.type === 'string') ? ' LOWER(?) ' : ' ? '; + let whereField = ''; + let whereValues = []; + value.where.field.forEach((f, i, arr) => { + whereField += (value.where.type === 'string') ? 'LOWER(' + tbl + '.' + value.where.field[i] + ')' : tbl + '.' + value.where.field[i]; + whereField += ' ' + value.where.relation + ' ?'; + if (i < arr.length - 1) { + whereField += ' ' + value.where.condition + ' '; + } + + if (Array.isArray(whereValue)) { + let whereString = '('; + for(let i = 0; i < whereValue.length; ++i) { + whereString += whereField; + whereValues.push(parseWhereValue(value.where.type, whereValue[i])); + if(i < whereValue.length-1) { + whereString += ' OR '; + } + } + whereString += ')'; + } else { + whereValues.push(parseWhereValue(value.where.type, whereValue)); + } + }); + + sql.where(whereField, ...whereValues); + } else { + let whereField = (value.where.type === 'string') ? 'LOWER(' + tbl + '.' + value.where.field + ')' : tbl + '.' + value.where.field; + let lower = (value.where.type === 'string') ? ' LOWER(?) ' : ' ? '; + if(Array.isArray(whereValue)) { + let whereString = '('; + let arrayWhereValues = []; + for(let i = 0; i < whereValue.length; ++i) { + whereString += whereField + ' ' + value.where.relation + lower; + arrayWhereValues.push(parseWhereValue(value.where.type, whereValue[i])); + if(i < whereValue.length-1) { + whereString += ' OR '; + } + } + whereString += ')'; + sql.where(whereString, ...arrayWhereValues); + } else { + sql.where(whereField + ' ' + value.where.relation + lower, parseWhereValue(value.where.type, whereValue)); + } + } + } + } + }); + }); + return sql; + } + + build() { + return (req, res, next) => { + req.sql = this.buildQuery(req, req.sql); + next(); + }; + } + + multibuild() { + return (req, res, next) => { + req.querySet.forEach((query) => { + query = this.buildQuery(req, query); + }); + next(); + }; + } +} + +module.exports = ReqQueryFields; diff --git a/src/libs/middlewares/response.js b/src/libs/middlewares/response.js index 580c16f5bd7470d20c622565954234f873f56b54..bd5009cd1e55ea391231348b30eea07568c6258d 100644 --- a/src/libs/middlewares/response.js +++ b/src/libs/middlewares/response.js @@ -7,12 +7,11 @@ const csv = require('csv-express'); // The function reads the req.query.format param and respond in json, xml or csv function response(value) { return (req, res, next) => { - log.debug(req.query.format); if (req.query.format === 'csv') { res.attachment(`${value}.csv`); - res.csv(req.result); + res.csv(req.result, true); } else if (req.query.format === 'xml') { - res.send(xml('result', JSON.stringify({ [value]: req.result }))); + res.send(xml.parse('result', { [value]: req.result })); } else { res.json({ result: req.result }); } diff --git a/src/libs/models/accessToken.js b/src/libs/models/accessToken.js new file mode 100644 index 0000000000000000000000000000000000000000..daab5898a66b952f72359d9685e951b1acf980b7 --- /dev/null +++ b/src/libs/models/accessToken.js @@ -0,0 +1,29 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; +const libs = `${process.cwd()}/libs`; +const User = require(`${libs}/models/user`); +const Client = require(`${libs}/models/client`); + +let AccessToken = new Schema({ + userId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'User' + }, + clientId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'Client' + }, + token: { + type: String, + unique: true, + required: true + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +module.exports = mongoose.model('AccessToken', AccessToken); diff --git a/src/libs/models/client.js b/src/libs/models/client.js new file mode 100644 index 0000000000000000000000000000000000000000..8ac80d8d158705f63efbbecc72de37d1fef6a3ac --- /dev/null +++ b/src/libs/models/client.js @@ -0,0 +1,17 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +let Client = new Schema({ + name: { + type: String, + unique: true, + required: true + }, + clientSecret: { + type: String, + required: true, + unique: true + } +}); + +module.exports = mongoose.model('Client', Client); diff --git a/src/libs/models/download.js b/src/libs/models/download.js new file mode 100644 index 0000000000000000000000000000000000000000..989896d9a2b6547c11c9b1bc71019db3bb7617a5 --- /dev/null +++ b/src/libs/models/download.js @@ -0,0 +1,53 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const User = require(`${libs}/models/user`); + +let Download = new Schema({ + userId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'User' + }, + table: { + type: String, + required: true + }, + name: { + type: String, + required: true + }, + mappingTable: { + type: String, + required: true + }, + query: { + type: String, + required: true + }, + createdAt: { + type: Date, + required: true, + default: Date.now + }, + updatedAt: { + type: Date, + required: true, + default: Date.now + }, + status: { + type: String + }, + size: { + type: Number + }, + expired: { + type: Boolean + }, + link: { + type: String + } +}); + +module.exports = mongoose.model('Download', Download); diff --git a/src/libs/models/pqr.js b/src/libs/models/pqr.js new file mode 100644 index 0000000000000000000000000000000000000000..f92703188323cef17807f70f1f2198b54a13aa1b --- /dev/null +++ b/src/libs/models/pqr.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose') + +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const User = require(`${libs}/models/user`); + +const Schema = mongoose.Schema; + +let PQRSchema = new Schema({ + content: { + type: String, + required: true, + } +}); + +module.exports = mongoose.model('PQR', PQRSchema); diff --git a/src/libs/models/refreshToken.js b/src/libs/models/refreshToken.js new file mode 100644 index 0000000000000000000000000000000000000000..c5f8fd63cdd30733bb60f341e6617420d945b037 --- /dev/null +++ b/src/libs/models/refreshToken.js @@ -0,0 +1,30 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; +const libs = `${process.cwd()}/libs`; +const User = require(`${libs}/models/user`); +const Client = require(`${libs}/models/client`); + +let RefreshToken = new Schema({ + userId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'User' + }, + clientId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'Client' + }, + token: { + type: String, + unique: true, + required: true + }, + createdAt: { + type: Date, + default: Date.now + } +}); + +module.exports = mongoose.model('RefreshToken', RefreshToken); + diff --git a/src/libs/models/resetToken.js b/src/libs/models/resetToken.js new file mode 100644 index 0000000000000000000000000000000000000000..322d528188a78525b4e003a65c6355fc0c2039c6 --- /dev/null +++ b/src/libs/models/resetToken.js @@ -0,0 +1,46 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const User = require(`${libs}/models/user`); +const uuid = require('node-uuid'); + +let ResetToken = new Schema({ + userId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'User' + }, + token: { + type: String, + required: true + }, + reset: { + type: Boolean, + required: true, + default: false + }, + createdAt: { + type: Date, + required: true, + default: Date.now + } +}); + +ResetToken.methods.createResetToken = function (done) { + let resetToken = this; + let token = uuid.v4(); + resetToken.set('token', token); + resetToken.reset = false; + resetToken.save(function(err) { + if (err) + return done(err); + return done(null, token); + }) +} +ResetToken.methods.hasExpired = function () { + var now = new Date(); + return (now - this.createdAt) > 86400; //Expire if token is 1 day old +}; + +module.exports = mongoose.model('ResetToken', ResetToken); diff --git a/src/libs/models/simulation.js b/src/libs/models/simulation.js new file mode 100644 index 0000000000000000000000000000000000000000..f1d3b0bd2448fe7640affd57e3d33d330ad7912c --- /dev/null +++ b/src/libs/models/simulation.js @@ -0,0 +1,34 @@ +const mongoose = require('mongoose') + +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const User = require(`${libs}/models/user`); + +const Schema = mongoose.Schema; + +let SimulationSchema = new Schema({ + userId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'User' + }, + content: { + type: String, + required: true, + }, + name: { + type: String + }, + createdAt: { + type: Date, + required: true, + default: Date.now + }, + updatedAt: { + type: Date, + required: true, + default: Date.now + } +}); + +module.exports = mongoose.model('Simulation', SimulationSchema); diff --git a/src/libs/models/user.js b/src/libs/models/user.js new file mode 100644 index 0000000000000000000000000000000000000000..0d54e4175fbf3336c4386bcc252d235dc6b478ce --- /dev/null +++ b/src/libs/models/user.js @@ -0,0 +1,119 @@ +const mongoose = require('mongoose'); +const crypto = require('crypto') +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const Schema = mongoose.Schema; + +// set up a mongoose model +var UserSchema = new Schema({ + email: { + type: String, + unique: true, + required: [true, 'O campo Email é obrigatório.'] + }, + hashedPassword: { + type: String, + required: [true, 'O campo Senha é obrigatório.'] + }, + salt: { + type: String, + required: true + }, + name: { + type: String, + required: [true, 'O campo Nome é obrigatório.'] + }, + nickname: { + type: String, + required: [true, 'O campo Apelido é obrigatório.'] + }, + cpf:{ + type: String, + unique: true, + required: [true, 'O campo CPF é obrigatório.'] + }, + cep:{ + type: String, + required: [true, 'O campo CEP é obrigatório.'] + }, + schooling: { + type: String, + required: [true, 'O campo Formação é obrigatório.'] + }, + course: { + type: String, + }, + complement: { + type: String, + }, + address: { + type: String, + }, + phone: { + type: String, + }, + segment: { + type: String, + required: [true, 'O campo Segmento é obrigatório.'] + }, + role: { + type: String, + required: [true, 'O campo Função é obrigatório.'] + }, + institutionName: { + type: String, + required: [true, 'O campo Instituição em que trabalha ou estuda é obrigatório.'] + }, + state: { + type: String, + required: [true, 'O campo Estado é obrigatório.'] + }, + city: { + type: String, + required: [true, 'O campo Cidade é obrigatório.'] + }, + receiveEmails: { + type: Boolean + }, + createdAt: { + type: Date, + default: Date.now + }, + origin: { + type: String, + enum: ['LDE', 'SimCAQ'], + required: [true, 'O campo origem é obrigatória e aceita apenas os valores "LDE" ou "SimCAQ"'] + }, + verified: { + type: Boolean, + default: false + }, + citesegment: { + type: String + }, + citerole: { + type: String + }, + admin: { + type: Boolean, + default: false + } +}); + +UserSchema.methods.encryptPassword = function(password) { + return crypto.pbkdf2Sync(password+'', this.salt, 10000, 512, 'sha512'); +}; + +UserSchema.virtual('password').set(function(password) { + this._plainPassword = password+''; + this.salt = crypto.randomBytes(128).toString('hex'); + this.hashedPassword = this.encryptPassword(password).toString('hex'); +}).get(function() { + return this._plainPassword; +}); + +UserSchema.methods.checkPassword = function(password) { + return this.encryptPassword(password).toString('hex') === this.hashedPassword; +} + +module.exports = mongoose.model('User', UserSchema); diff --git a/src/libs/models/verificationToken.js b/src/libs/models/verificationToken.js new file mode 100644 index 0000000000000000000000000000000000000000..ef7e109e16364874ea7d3f4b07b6e19ce74c3eae --- /dev/null +++ b/src/libs/models/verificationToken.js @@ -0,0 +1,41 @@ +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; +const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); +const User = require(`${libs}/models/user`); +const uuid = require('node-uuid'); + +let VerificationToken = new Schema({ + userId: { + type: Schema.Types.ObjectId, + required: true, + ref: 'User' + }, + token: { + type: String, + required: true + }, + verified: { + type: Boolean, + required: true, + default: false + }, + createdAt: { + type: Date, + required: true, + default: Date.now + } +}); + +VerificationToken.methods.createVerificationToken = function(done) { + let verificationToken = this; + let token = uuid.v4(); + verificationToken.set('token', token); + verificationToken.verified = false; + verificationToken.save(function(err) { + if (err) return done(err); + return done(null, token); + }) +} + +module.exports = mongoose.model('VerificationToken', VerificationToken); diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js index 3faaa926c7055bc9849d4457e183e8ea6fd48cf8..2b56919393323db34a82b3f4373f39c1684a28dc 100644 --- a/src/libs/routes/api.js +++ b/src/libs/routes/api.js @@ -2,6 +2,12 @@ const express = require('express'); const api = express(); +const libs = `${process.cwd()}/libs`; + +const config = require(`${libs}/config`); + +const classes = require('./class'); + const enrollment = require('./enrollment'); const state = require('./state'); @@ -12,15 +18,79 @@ const city = require('./city'); const school = require('./school'); +const simulation = require('./simulation'); + +const user = require('./user'); + +const classroom = require('./classroom'); + +const teacher = require('./teacher'); + +const idhme = require('./idhme'); + +const pibpercapita = require('./pibpercapita') + +const population = require('./population') + +const rateSchool = require('./rateSchool') + +const glossEnrollmentRatio = require('./glossEnrollmentRatio') + +const liquidEnrollmentRatio = require('./liquidEnrollmentRatio') + +const idhm = require('./idhm'); + +const idhmr = require('./idhmr'); + +const idhml = require('./idhml'); + +const oauth2 = require(`${libs}/middlewares/oauth2`); + +const verifyToken = require(`${libs}/routes/verifyToken`); + +const resetToken = require(`${libs}/routes/resetToken`); + +const educationYears = require(`${libs}/routes/educationYears`); + +const downloads = require(`${libs}/routes/downloads`); + +const infrastructure = require(`${libs}/routes/infrastructure`); + +const distributionFactor = require(`${libs}/routes/distributionFactor`); + +const siope = require(`${libs}/routes/siope`); + api.get('/', (req, res) => { res.json({ msg: 'SimCAQ API is running' }); }); // mount API routes +api.use('/user', user); +api.use('/simulation', simulation); +api.use('/class', classes); api.use('/enrollment', enrollment); api.use('/state', state); api.use('/region', region); api.use('/city', city); api.use('/school', school); +api.use('/classroom', classroom); +api.use('/teacher', teacher); +api.use('/idhmr', idhmr); +api.use('/idhm', idhm); +api.use('/idhme', idhme); +api.use('/pibpercapita', pibpercapita); +api.use('/population', population); +api.use('/rate_school', rateSchool); +api.use('/gloss_enrollment_ratio', glossEnrollmentRatio); +api.use('/liquid_enrollment_ratio', liquidEnrollmentRatio); +api.use('/idhml', idhml); +api.use('/auth/token', oauth2.token); +api.use('/verify', verifyToken); +api.use('/reset', resetToken); +api.use('/education_years', educationYears); +api.use('/downloads', downloads); +api.use('/infrastructure', infrastructure); +api.use('/distribution_factor', distributionFactor); +api.use('/siope', siope); module.exports = api; diff --git a/src/libs/routes/city.js b/src/libs/routes/city.js index 733da6f341cc37a7b6b792b8f5383076eae75067..1f6aee12630ba58b48f2f53bb17fc9ab10b4fad1 100644 --- a/src/libs/routes/city.js +++ b/src/libs/routes/city.js @@ -6,34 +6,70 @@ const libs = `${process.cwd()}/libs`; const squel = require('squel'); -const query = require(`${libs}/middlewares/query`); +const query = require(`${libs}/middlewares/query`).query; const response = require(`${libs}/middlewares/response`); -// Return all cities -cityApp.get('/', (req, res, next) => { - req.sql.from('municipios'); - next(); -}, query, response('city')); +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); -// Return a specific city by it's id -cityApp.get('/:id', (req, res, next) => { - req.sql.from('municipios') - .where('pk_municipio_id = ?', parseInt(req.params.id, 10)); - next(); -}, query, response('city')); +const config = require(`${libs}/config`); -// Return a specific city by it's IBGE code -cityApp.get('/ibge/:id', (req, res, next) => { - req.sql.from('municipios') - .where('codigo_ibge = ?', req.params.id); - next(); -}, query, response('city')); +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +cityApp.use(cache('15 day')); -// Return all the cities from a specific state -cityApp.get('/state/:id', (req, res, next) => { - req.sql.from('municipios') - .where('fk_estado_id = ?', parseInt(req.params.id, 10)); +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addValue({ + name: 'id', + table: 'municipio', + tableField: 'id', + where: { + relation: '=', + type: 'integer', + field: 'id' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'municipio' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'municipio' + } +}).addField({ + name: 'search', + field: false, + where: true +}).addValueToField({ + name: 'name', + table: 'municipio', + tableField: 'nome', + where: { + relation: 'LIKE', + type: 'string', + field: 'nome' + } +}, 'search');; + +// Return all cities +cityApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + req.sql.from('municipio') + .field('municipio.nome', 'name') + .field('municipio.id') + .field('municipio.estado_id', 'state_id'); next(); }, query, response('city')); diff --git a/src/libs/routes/class.js b/src/libs/routes/class.js new file mode 100644 index 0000000000000000000000000000000000000000..dae41b6ee89d8d2e70421f3f9ba0cc38e305fb6b --- /dev/null +++ b/src/libs/routes/class.js @@ -0,0 +1,359 @@ +const express = require('express'); + +const classApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const config = require(`${libs}/config`); + +const download = require(`${libs}/middlewares/downloadDatabase`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const passport = require('passport'); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqfCount = new ReqQueryFields(); + +classApp.use(cache('15 day')); + +// Complete range of the enrollments dataset. +// Returns a tuple of start and ending years of the complete enrollments dataset. +classApp.get('/year_range', (req, res, next) => { + req.sql.from('turma') + .field('MIN(turma.ano_censo)', 'start_year') + .field('MAX(turma.ano_censo)', 'end_year'); + next(); +}, query, response('range')); + +classApp.get('/years', (req, res, next) => { + req.sql.from('turma') + .field('DISTINCT turma.ano_censo', 'year'); + next(); +}, query, response('years')); + +classApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'turma\''); + next(); +}, query, response('source')); + +classApp.get('/location', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 2; ++i) { + req.result.push({ + id: i, + name: id2str.location(i) + }); + }; + next(); +}, response('location')); + +classApp.get('/rural_location', (req, res, next) => { + req.result = [ + {id: 1, name: "Urbana"}, + {id: 2, name: "Rural"}, + {id: 3, name: "Rural - Área de assentamento"}, + {id: 4, name: "Rural - Terra indígena"}, + {id: 5, name: "Rural - Área remanescente de quilombos"}, + {id: 6, name: "Rural - Unidade de uso sustentável"} + ]; + next(); +}, response('rural_location')); + +// Returns all adm dependencies +classApp.get('/adm_dependency', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 4; ++i) { + req.result.push({ + id: i, + name: id2str.admDependency(i) + }); + }; + next(); +}, response('adm_dependency')); + +classApp.get('/adm_dependency_detailed', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 6; ++i) { + req.result.push({ + id: i, + name: id2str.admDependencyPriv(i) + }); + }; + next(); +}, response('adm_dependency_detailed')); + +// Returns all periods avaible +classApp.get('/period', (req, res, next) => { + req.result = []; + for(let i=1; i <= 3; ++i) { + req.result.push({ + id: i, + name: id2str.period(i) + }); + } + req.result.push({ + id: 99, + name: id2str.period(99) + }); + next(); +}, response('period')); + +// Returns integral-time avaible +classApp.get('/integral_time', (req, res, next) => { + req.result = [ + {id: null, name: 'Não Disponível'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('integral_time')); + +// Returns all educational levels avaible +classApp.get('/education_level_mod', (req, res, next) => { + req.result = []; + for(let i = 1; i <=11; ++i) { + req.result.push({ + id: i, + name: id2str.educationLevelMod(i) + }); + } + req.result.push({ + id: 99, + name: id2str.educationLevelMod(99) + }); + next(); +}, response('education_level_mod')); + +classApp.get('/education_level_short', (req, res, next) => { + req.result = [ + {id: null, name: 'Não classificada'}, + {id: 1, name: 'Creche'}, + {id: 2, name: 'Pré-Escola'}, + {id: 3, name: 'Ensino Fundamental - anos iniciais'}, + {id: 4, name: 'Ensino Fundamental - anos finais'}, + {id: 5, name: 'Ensino Médio'}, + {id: 6, name: 'EJA'}, + {id: 7, name: 'EE exclusiva'} + ]; + next(); +}, response('education_level_short')); + +rqfCount.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValueToField({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'turma' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'turma' + } +}, 'dims').addValueToField({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'turma' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'turma' + } +}, 'filter').addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'turma' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'turma' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'turma' + } +}).addValue({ + name: 'min_year', + table: 'turma', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: 'turma', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name:'adm_dependency', + table: 'turma', + tableField: 'dependencia_adm_id', + resultField: 'adm_dependency_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_id' + } +}).addValue({ + name: 'location', + table: 'turma', + tableField: 'localizacao_id', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'localizacao_id' + } +}).addValue({ + name: 'rural_location', + table: 'turma', + tableField: 'localidade_area_rural', + resultField: 'rural_location_id', + where: { + relation: '=', + type: 'integer', + field: 'localidade_area_rural' + } +}).addValue({ + name:'education_level_mod', + table: 'turma', + tableField: 'etapas_mod_ensino_segmento_id', + resultField: 'education_level_mod_id', + where: { + relation: '=', + type: 'integer', + field: 'etapas_mod_ensino_segmento_id' + } +}).addValue({ + name: 'education_level_short', + table: 'turma', + tableField: 'etapa_resumida', + resultField: 'education_level_short_id', + where: { + relation: '=', + type: 'integer', + field: 'etapa_resumida' + } +}).addValue({ + name: 'adm_dependency_detailed', + table: 'turma', + tableField: 'dependencia_adm_priv', + resultField: 'adm_dependency_detailed_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_priv' + } +}).addValue({ + name:'period', + table: 'turma', + tableField: 'turma_turno_id', + resultField: 'period_id', + where: { + relation: '=', + type: 'integer', + field: 'turma_turno_id' + } +}).addValue({ + name:'integral_time', + table: 'turma', + tableField: 'tempo_integral', + resultField: 'integral_time_id', + where: { + relation: '=', + type: 'boolean', + field: 'tempo_integral' + } +}).addValue({ + name: 'school', + table: 'escola', + tableField: 'nome_escola', + resultField: 'school_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: ['id', 'ano_censo'], + foreign: ['escola_id', 'ano_censo'], + foreignTable: 'turma' + } +}); + + +classApp.get('/', rqfCount.parse(), rqfCount.build(), (req, res, next) => { + req.sql.field('COUNT(turma.id)', 'total') + .field("'Brasil'", 'name') + .field('turma.ano_censo', 'year') + .from('turma') + .group('turma.ano_censo') + .order('turma.ano_censo') + .where('turma.tipo_turma_id = 0 OR turma.tipo_turma_id = 1 OR turma.tipo_turma_id = 2 OR turma.tipo_turma_id = 3'); + next(); +}, query, addMissing(rqfCount), id2str.transform(), response('class')); + +classApp.get('/download', passport.authenticate('bearer', { session: false }), rqfCount.parse(), rqfCount.build(), download('turma', 'mapping_turma')); + +module.exports = classApp; diff --git a/src/libs/routes/classroom.js b/src/libs/routes/classroom.js new file mode 100644 index 0000000000000000000000000000000000000000..246e3d9bc7ae1a54c54a3afcc1104e0190107a13 --- /dev/null +++ b/src/libs/routes/classroom.js @@ -0,0 +1,233 @@ + const express = require('express'); + +const classroomApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +let rqfCount = new ReqQueryFields(); + +// Complete range of the enrollments dataset. +// Returns a tuple of start and ending years of the complete enrollments dataset. +classroomApp.get('/year_range', (req, res, next) => { + req.sql.from('escola') + .field('MIN(escola.ano_censo)', 'start_year') + .field('MAX(escola.ano_censo)', 'end_year'); + next(); +}, query, response('range')); + +classroomApp.get('/years', (req, res, next) => { + req.sql.from('escola') + .field('DISTINCT escola.ano_censo', 'year'); + next(); +}, query, response('years')); + +classroomApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'escola\''); + next(); +}, query, response('source')); + +classroomApp.get('/adm_dependency', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 4; ++i) { + req.result.push({ + id: i, + name: id2str.admDependency(i) + }); + }; + next(); +}, response('adm_dependency')); + +classroomApp.get('/adm_dependency_detailed', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 6; ++i) { + req.result.push({ + id: i, + name: id2str.admDependencyPriv(i) + }); + }; + next(); +}, response('adm_dependency_detailed')); + +classroomApp.get('/location', (req, res, next) => { + req.result = [ + {id: 1, name: 'Urbana'}, + {id: 2, name: 'Rural'} + ]; + next(); +}, response('location')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValueToField({ + name: 'school', + table: 'escola', + tableField: ['nome_escola', 'id'], + resultField: ['school_name', 'school_id'], + where: { + relation: '=', + type: 'integer', + field: 'id' + } +}, 'dims').addValueToField({ + name: 'school', + table: 'escola', + tableField: 'nome_escola', + resultField: 'school_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + } +}, 'filter').addValueToField({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'escola' + } +}, 'dims').addValueToField({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'escola' + } +}, 'filter').addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'escola' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'escola' + } +}).addValue({ + name: 'min_year', + table: 'escola', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: 'escola', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'adm_dependency', + table: 'escola', + tableField: 'dependencia_adm_id', + resultField: 'adm_dependency_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_id' + } +}).addValue({ + name: 'adm_dependency_detailed', + table: 'escola', + tableField: 'dependencia_adm_priv', + resultField: 'adm_dependency_detailed_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_priv' + } +}).addValue({ + name: 'location', + table: 'escola', + tableField: 'cod_localizacao', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'cod_localizacao' + } +}); + +classroomApp.get('/', cache('15 day'), rqf.parse(), rqf.build(), (req, res, next) => { + req.sql.from('escola') + .field('SUM(escola.num_salas)', 'total') + .field("'Brasil'", 'name') + .field('escola.ano_censo', 'year') + .group('escola.ano_censo') + .order('escola.ano_censo') + .where('escola.situacao_de_funcionamento = 1 AND escola.local_func_predio_escolar = 1'); + next(); +}, query, addMissing(rqf), id2str.transform(), response('classroom')); + +module.exports = classroomApp; diff --git a/src/libs/routes/distributionFactor.js b/src/libs/routes/distributionFactor.js new file mode 100644 index 0000000000000000000000000000000000000000..df4d84b797884f71f92e55456ed365317d98679a --- /dev/null +++ b/src/libs/routes/distributionFactor.js @@ -0,0 +1,190 @@ +const express = require('express'); + +const distributionApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const multiQuery = require(`${libs}/middlewares/multiQuery`); + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +distributionApp.use(cache('15 day')); + +rqf.addField({ + name: 'dims', + field: true, + where: false +}).addField({ + name: 'filter', + field: false, + where: true +}).addValueToField({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'fatores_matricula' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'fatores_matricula' + } +}, 'dims').addValueToField({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'fatores_matricula' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'fatores_matricula' + } +}, 'filter').addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'fatores_matricula' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'fatores_matricula' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'fatores_matricula' + } +}) + +// Return all cities +distributionApp.get('/', rqf.parse(), (req, res, next) => { + req.querySet = []; + req.queryIndex = {}; + + let relation = req.sql.clone(); + relation.from('relacao_fatores_matricula').field('*'); + req.queryIndex.relation = req.querySet.push(relation) - 1; + + req.sql.from('fatores_matricula') + .field('fatores_matricula.municipio_id', 'municipio_id') + .field('fatores_matricula."mais_CRE_0"') + .field('fatores_matricula."mais_CRE_1"') + .field('fatores_matricula."mais_CRE_2"') + .field('fatores_matricula."mais_CRE_3"') + .field('fatores_matricula."mais_PRE"') + .field('fatores_matricula."mais_EFAI"') + .field('fatores_matricula."mais_EFAF"') + .field('fatores_matricula."mais_EM"') + .field('fatores_matricula."mais_EJA"') + .field('fatores_matricula."menos_CRE_0"') + .field('fatores_matricula."menos_CRE_1"') + .field('fatores_matricula."menos_CRE_2"') + .field('fatores_matricula."menos_CRE_3"') + .field('fatores_matricula."menos_PRE"') + .field('fatores_matricula."menos_EFAI"') + .field('fatores_matricula."menos_EFAF"') + .field('fatores_matricula."menos_EM"') + .field('fatores_matricula."menos_EJA"') + .group('fatores_matricula.municipio_id') + .group('fatores_matricula."mais_CRE_0"') + .group('fatores_matricula."mais_CRE_1"') + .group('fatores_matricula."mais_CRE_2"') + .group('fatores_matricula."mais_CRE_3"') + .group('fatores_matricula."mais_PRE"') + .group('fatores_matricula."mais_EFAI"') + .group('fatores_matricula."mais_EFAF"') + .group('fatores_matricula."mais_EM"') + .group('fatores_matricula."mais_EJA"') + .group('fatores_matricula."menos_CRE_0"') + .group('fatores_matricula."menos_CRE_1"') + .group('fatores_matricula."menos_CRE_2"') + .group('fatores_matricula."menos_CRE_3"') + .group('fatores_matricula."menos_PRE"') + .group('fatores_matricula."menos_EFAI"') + .group('fatores_matricula."menos_EFAF"') + .group('fatores_matricula."menos_EM"') + .group('fatores_matricula."menos_EJA"'); + + if(typeof req.dims.state !== 'undefined' || typeof req.filter.state !== 'undefined') { + req.sql.where('fatores_matricula.nivel = \'UF\''); + } else { + req.sql.where('fatores_matricula.nivel = \'BR\''); + } + + next(); +}, rqf.build(), query, (req, res, next) => { + req.enrollmentFactor = req.result; + next(); +}, multiQuery, (req, res, next) => { + let relation = req.result[req.queryIndex.relation]; + let result = []; + let first = true; + req.enrollmentFactor.forEach((city) => { + // if(first) console.log(city); + let obj = { + level: city.nivel, + region_id: city.regiao_id, + region_name: city.region_name, + state_id: city.state_id, + state_name: city.state_name, + city_id: city.municipio_id, + city_name: city.city_name, + series: [] + }; + // if(first) console.log(obj); + first = false; + relation.forEach((serie) => { + obj.series.push({ + serie_id: serie.id, + distribution_factor_addition: city[serie.fator_adicao], + distribution_factor_reduction: city[serie.fator_reducao] + }); + }); + result.push(obj); + }); + req.result = result; + next(); +}, response('ditributionFactor')); + +module.exports = distributionApp; diff --git a/src/libs/routes/downloads.js b/src/libs/routes/downloads.js new file mode 100644 index 0000000000000000000000000000000000000000..2ec83b9ee7ffa82bbaf3e873c949c842dd6b6ab1 --- /dev/null +++ b/src/libs/routes/downloads.js @@ -0,0 +1,54 @@ +const express = require('express'); + +const downloadApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const Download = require(`${libs}/models/download`); + +const User = require(`${libs}/models/user`); + +const passport = require('passport'); + +const request = require(`request`); + +const config = require(`${libs}/config`); + +downloadApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, next) => { + request.get(config.cdn.url + '/api/v1/file', (err, response, body) => { + let cdn = JSON.parse(body); + Download.find({userId: req.user._id}, (err, downloads) => { + if (err) { + log.error(err); + return next(err); + } + + if(!downloads) { + res.statusCode = 404; + return res.json({msg: 'Nenhum download encontrado'}); + } else { + downloads.forEach((dl) => { + for(let i = 0; i < cdn.length; ++i) { + if(cdn[i].query == dl.query) { + dl.status = cdn[i].expired ? 'Expirado' : 'Enviado'; + dl.size = cdn[i].size; + dl.expired = cdn[i].expired; + dl.updatedAt = cdn[i].lastAccess; + dl.link = config.cdn.download + '/' + cdn[i]._id; + + dl.save((err) => { + if(err) log.error(err); + }); + return; + } + } + }); + } + res.json(downloads); + }); + }); +}); + +module.exports = downloadApp; diff --git a/src/libs/routes/educationYears.js b/src/libs/routes/educationYears.js new file mode 100644 index 0000000000000000000000000000000000000000..0d103883f53fc53dbd8770fa983dc5fe568564dd --- /dev/null +++ b/src/libs/routes/educationYears.js @@ -0,0 +1,46 @@ +const express = require('express'); + +const educationYearsApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const response = require(`${libs}/middlewares/response`); + +const config = require(`${libs}/config`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +educationYearsApp.use(cache('15 day')); + +educationYearsApp.get('/', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 7; ++i) { + let edLvlShort = { + id: i, + name: id2str.educationLevelShort(i), + schoolYears: [] + }; + + for(let j = i*10; j <= (i*10 + 9); ++j) { + + let schoolYear = { + id: j, + name: id2str.schoolYear(j) + }; + + if(schoolYear.name !== id2str.schoolYear(99)) { + edLvlShort.schoolYears.push(schoolYear); + } + } + if(edLvlShort.name !== id2str.schoolYear(99)) { + req.result.push(edLvlShort); + } + } + next(); +}, response('educationYears')); + +module.exports = educationYearsApp; diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js index 9fe6e903bdbe9bc10b89181866a920792b832533..a3a25119d7067e08456e71056a3cff02a99b743b 100644 --- a/src/libs/routes/enrollment.js +++ b/src/libs/routes/enrollment.js @@ -8,207 +8,463 @@ const log = require(`${libs}/log`)(module); const squel = require('squel'); -const query = require(`${libs}/middlewares/query`); +const query = require(`${libs}/middlewares/query`).query; const response = require(`${libs}/middlewares/response`); -const parseParams = require(`${libs}/middlewares/parseParams`); +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); -// **Temporary** solution to add where clauses that are common to all requests +const id2str = require(`${libs}/middlewares/id2str`); +const config = require(`${libs}/config`); + +const passport = require('passport'); + +const download = require(`${libs}/middlewares/downloadDatabase`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +enrollmentApp.use(cache('15 day')); + +let rqf = new ReqQueryFields(); // Complete range of the enrollments dataset. // Returns a tuple of start and ending years of the complete enrollments dataset. enrollmentApp.get('/year_range', (req, res, next) => { - req.sql = squel.select() - .from('turmas') - .field('MIN(turmas.ano_censo)', 'start_year') - .field('MAX(turmas.ano_censo)', 'end_year'); - + req.sql.from('matricula') + .field('MIN(matricula.ano_censo)', 'start_year') + .field('MAX(matricula.ano_censo)', 'end_year'); next(); }, query, response('range')); -// Returns all educational levels avaible -enrollmentApp.get('/education_level', (req, res, next) => { - req.sql = squel.select() - .from('etapa_ensino') - .field('pk_etapa_ensino_id', 'id') - .field('desc_etapa', 'name'); - +enrollmentApp.get('/years', (req, res, next) => { + req.sql.from('matricula') + .field('DISTINCT matricula.ano_censo', 'year'); next(); -}, query, response('education_level')); - -// Returns all adm dependencies -enrollmentApp.get('/adm_dependency', (req, res, next) => { - req.sql = squel.select() - .from('dependencia_adms') - .field('pk_dependencia_adm_id', 'id') - .field('nome', 'name'); +}, query, response('years')); +enrollmentApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'matricula\''); next(); -}, query, response('adm_dependency')); - -// Parse the filters and dimensions parameter in the query -enrollmentApp.use('/', parseParams('filter', [ - 'min_year', - 'max_year', - 'adm_dependency_id', - 'location_id', - 'education_level_id', - 'region', - 'state', - 'city', - 'school' -]), parseParams('dims', [ - 'adm_dependency_id', - 'location_id', - 'education_level_id', - 'region', - 'state', - 'city', - 'school' -]), (req, res, next) => { - log.debug(req.filter); - log.debug(req.dims); - - // Do the joins - if(typeof req.filter.adm_dependency_id !== 'undefined' - || typeof req.dims.adm_dependency_id !== 'undefined') { - req.sql.join('dependencia_adms', null, 'fk_dependencia_adm_id=dependencia_adms.pk_dependencia_adm_id'); +}, query, response('source')); + +enrollmentApp.get('/location', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 2; ++i) { + req.result.push({ + id: i, + name: id2str.location(i) + }); + }; + next(); +}, response('location')); + +enrollmentApp.get('/rural_location', (req, res, next) => { + req.result = [ + {id: 1, name: "Urbana"}, + {id: 2, name: "Rural"}, + {id: 3, name: "Rural - Área de assentamento"}, + {id: 4, name: "Rural - Terra indígena"}, + {id: 5, name: "Rural - Área remanescente de quilombos"}, + {id: 6, name: "Rural - Unidade de uso sustentável"} + ]; + next(); +}, response('rural_location')); + +// Returns all school years available +enrollmentApp.get('/school_year', (req, res, next) => { + req.result = []; + for(let i = 11; i <= 71; ++i) { + let obj = { + id: i, + name: id2str.schoolYear(i) + }; + + if(obj.name !== id2str.schoolYear(99)) { + req.result.push(obj); + } } + req.result.push({ + id: 99, + name: id2str.schoolYear(99) + }); + next(); +}, response('school_year')); - if(typeof req.filter.education_level_id !== 'undefined' - || typeof req.dims.education_level_id !== 'undefined') { - req.sql.join('etapa_ensino', null, 'fk_etapa_ensino_id=etapa_ensino.pk_etapa_ensino_id'); +// Returns all school years available +enrollmentApp.get('/education_level', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 74; ++i) { + let obj = { + id: i, + name: id2str.educationLevel(i) + }; + + if(obj.name !== id2str.educationLevel(99)) { + req.result.push(obj); + } } - - if(typeof req.filter.region !== 'undefined' - || typeof req.dims.region !== 'undefined') { - req.sql.join('municipios', null, 'fk_municipio_id=municipios.pk_municipio_id') - .join('estados', null, 'municipios.fk_estado_id=estados.pk_estado_id') - .join('regioes', null, 'estados.fk_regiao_id=regioes.pk_regiao_id'); + req.result.push({ + id: 99, + name: id2str.educationLevel(99) + }); + next(); +}, response('education_level')); + +// Returns all school years available +enrollmentApp.get('/education_level_mod', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 10; ++i) { + req.result.push({ + id: i, + name: id2str.educationLevelMod(i) + }); } + req.result.push({ + id: 99, + name: id2str.educationLevelMod(99) + }); + next(); +}, response('education_level_mod')); + +enrollmentApp.get('/education_level_short', (req, res, next) => { + req.result = [ + {id: null, name: 'Não classificada'}, + {id: 1, name: 'Creche'}, + {id: 2, name: 'Pré-Escola'}, + {id: 3, name: 'Ensino Fundamental - anos iniciais'}, + {id: 4, name: 'Ensino Fundamental - anos finais'}, + {id: 5, name: 'Ensino Médio'}, + {id: 6, name: 'EJA'}, + {id: 7, name: 'EE exclusiva'} + ]; + next(); +}, response('education_level_short')); - if((typeof req.filter.state !== 'undefined' - || typeof req.dims.state !== 'undefined') - && (typeof req.filter.region === 'undefined' - && typeof req.dims.region === 'undefined')) { - req.sql.join('municipios', null, 'fk_municipio_id=municipios.pk_municipio_id') - .join('estados', null, 'municipios.fk_estado_id=estados.pk_estado_id'); +// Returns all adm dependencies +enrollmentApp.get('/adm_dependency', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 4; ++i) { + req.result.push({ + id: i, + name: id2str.admDependency(i) + }); + }; + next(); +}, response('adm_dependency')); + +enrollmentApp.get('/adm_dependency_detailed', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 6; ++i) { + req.result.push({ + id: i, + name: id2str.admDependencyPriv(i) + }); + }; + next(); +}, response('adm_dependency_detailed')); + +// Return genders +enrollmentApp.get('/gender', (req, res, next) => { + req.result = [ + {id: 1, name: 'Masculino'}, + {id: 2, name: 'Feminino'} + ]; + next(); +}, response('gender')); + +// Return ethnic group +enrollmentApp.get('/ethnic_group', (req, res, next) => { + req.result = []; + for(let i = 1; i <=5; ++i) { + req.result.push({ + id: i, + name: id2str.ethnicGroup(i) + }); } - - if((typeof req.filter.city !== 'undefined' - || typeof req.dims.city !== 'undefined') - && (typeof req.filter.state === 'undefined' - && typeof req.dims.state === 'undefined') - && (typeof req.filter.region === 'undefined' - && typeof req.dims.region === 'undefined')) { - req.sql.join('municipios', null, 'fk_municipio_id=municipios.pk_municipio_id'); + next(); +}, response('ethnic_group')); + +enrollmentApp.get('/period', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 3; ++i) { + req.result.push({ + id: i, + name: id2str.period(i) + }); } - - if(typeof req.dims.school !== 'undefined') { - req.sql.join('escolas', null, 'fk_escola_id=escolas.pk_escola_id'); + req.result.push({ + id: 99, + name: id2str.period(99) + }); + next(); +}, response('period')); + +// Returns integral-time avaible +enrollmentApp.get('/integral_time', (req, res, next) => { + req.result = [ + {id: null, name: 'Não Disponível'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('integral_time')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'adm_dependency', + table: 'matricula', + tableField: 'dependencia_adm_id', + resultField: 'adm_dependency_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_id' } - - // Dimensions (add fields) - - if(typeof req.dims.education_level_id !== 'undefined') { - req.sql.field('desc_etapa', 'education_level') - .group('desc_etapa') - .order('desc_etapa'); +}).addValue({ + name: 'adm_dependency_detailed', + table: 'matricula', + tableField: 'dependencia_adm_priv', + resultField: 'adm_dependency_detailed_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_priv' } - - if(typeof req.dims.region !== 'undefined') { - req.sql.field('regioes.nome', 'region_name') - .group('regioes.nome') - .order('regioes.nome'); +}).addValue({ + name: 'school_year', + table: 'matricula', + tableField: 'serie_ano_id', + resultField: 'school_year_id', + where: { + relation: '=', + type: 'integer', + field: 'serie_ano_id' } - - if(typeof req.dims.state !== 'undefined') { - req.sql.field('estados.nome', 'state_name') - .group('estados.nome') - .order('estados.nome'); +}).addValue({ + name: 'education_level', + table: 'matricula', + tableField: 'etapa_ensino_id', + resultField: 'education_level_id', + where: { + relation: '=', + type: 'integer', + field: 'etapa_ensino_id' } - - if(typeof req.dims.city !== 'undefined') { - req.sql.field('municipios.nome', 'city_name') - .group('municipios.nome') - .order('municipios.nome'); +}).addValue({ + name: 'education_level_mod', + table: 'matricula', + tableField: 'etapas_mod_ensino_segmento_id', + resultField: 'education_level_mod_id', + where: { + relation: '=', + type: 'integer', + field: 'etapas_mod_ensino_segmento_id' } - - if(typeof req.dims.school !== 'undefined') { - req.sql.field('escolas.nome_entidade', 'school_name') - .group('escolas.nome_entidade') - .order('escolas.nome_entidade'); +}).addValue({ + name: 'education_level_short', + table: 'matricula', + tableField: 'etapa_resumida', + resultField: 'education_level_short_id', + where: { + relation: '=', + type: 'integer', + field: 'etapa_resumida' } - - if(typeof req.dims.adm_dependency_id !== 'undefined') { - req.sql.field('dependencia_adms.nome', 'adm_dependency_name') - .group('dependencia_adms.nome') - .order('dependencia_adms.nome'); +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'matricula' } - - if(typeof req.dims.location_id !== 'undefined') { - req.sql.field('turmas.id_localizacao', 'location') - .group('turmas.id_localizacao') - .order('turmas.id_localizacao'); +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'matricula' } - - if(typeof req.dims.region === 'undefined' - && typeof req.dims.state === 'undefined' - && typeof req.dims.city === 'undefined' - && typeof req.dims.school === 'undefined') { - req.sql.field("'Brasil'", 'name'); +}).addValueToField({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'matricula' } - - // Filter (add where) - - if (typeof req.filter.min_year !== 'undefined') { - req.sql.where('turmas.ano_censo>=?', parseInt(req.filter.min_year, 10)); +}, 'dims').addValueToField({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'matricula' } - - if (typeof req.filter.max_year !== 'undefined') { - req.sql.where('turmas.ano_censo<=?', parseInt(req.filter.max_year, 10)); +}, 'filter').addValueToField({ + name: 'school', + table: 'escola', + tableField: ['nome_escola', 'id'], + resultField: ['school_name', 'school_id'], + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: ['id', 'ano_censo'], + foreign: ['escola_id', 'ano_censo'], + foreignTable: 'matricula' } - - if (typeof req.filter.adm_dependency_id !== 'undefined') { - req.sql.where('pk_dependencia_adm_id=?', parseInt(req.filter.adm_dependency_id, 10)); +}, 'dims').addValueToField({ + name: 'school', + table: 'escola', + tableField: 'nome_escola', + resultField: 'school_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: ['id', 'ano_censo'], + foreign: ['escola_id', 'ano_censo'], + foreignTable: 'matricula' } - - if (typeof req.filter.location_id !== 'undefined') { - req.sql.where('turmas.id_localizacao=?', parseInt(req.filter.location_id, 10)); +}, 'filter').addValue({ + name: 'location', + table: 'matricula', + tableField: 'localizacao_id', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'localizacao_id' } - - if (typeof req.filter.education_level_id !== 'undefined') { - req.sql.where('pk_etapa_ensino_id=?', parseInt(req.filter.education_level_id, 10)); +}).addValue({ + name: 'rural_location', + table: 'matricula', + tableField: 'localidade_area_rural', + resultField: 'rural_location_id', + where: { + relation: '=', + type: 'integer', + field: 'localidade_area_rural' } - - if (typeof req.filter.region !== 'undefined') { - req.sql.where('pk_regiao_id=?', parseInt(req.filter.region, 10)); +}).addValue({ + name: 'min_year', + table: 'matricula', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + field: 'ano_censo' } - - if (typeof req.filter.state !== 'undefined') { - req.sql.where('pk_estado_id=?', parseInt(req.filter.state, 10)); +}).addValue({ + name: 'max_year', + table: 'matricula', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + field: 'ano_censo' } - - if (typeof req.filter.city !== 'undefined') { - req.sql.where('turmas.fk_municipio_id=?', parseInt(req.filter.city, 10)); +}).addValue({ + name: 'gender', + table: 'matricula', + tableField: 'sexo', + resultField: 'gender_id', + where: { + relation: '=', + type: 'integer', + field: 'sexo' } - - if (typeof req.filter.school !== 'undefined') { - req.sql.where('turmas.fk_escola_id=?', parseInt(req.filter.school, 10)); +}).addValue({ + name: 'ethnic_group', + table: 'matricula', + tableField: 'cor_raca_id', + resultField: 'ethnic_group_id', + where: { + relation: '=', + type: 'integer', + field: 'cor_raca_id' } - log.debug(req.sql.toParam()); - next(); +}).addValue({ + name: 'period', + table: 'matricula', + tableField: 'turma_turno_id', + resultField: 'period_id', + where: { + relation: '=', + type: 'integer', + field: 'turma_turno_id' + } +}).addValue({ + name:'integral_time', + table: 'matricula', + tableField: 'tempo_integral', + resultField: 'integral_time_id', + where: { + relation: '=', + type: 'boolean', + field: 'tempo_integral' + } }); -enrollmentApp.get('/', (req, res, next) => { - req.sql.field('COALESCE(SUM(num_matriculas), 0)', 'total') - .field('turmas.ano_censo', 'year') - .from('turmas') - .group('turmas.ano_censo') - .order('turmas.ano_censo'); +enrollmentApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + req.sql.field('COUNT(*)', 'total') + .field("'Brasil'", 'name') + .field('matricula.ano_censo', 'year') + .from('matricula') + .group('matricula.ano_censo') + .order('matricula.ano_censo') + .where('matricula.tipo<=3'); next(); -}, query, response('test')); +}, query, addMissing(rqf), id2str.transform(false), response('enrollment')); + +enrollmentApp.get('/download', passport.authenticate('bearer', { session: false }), rqf.parse(), rqf.build(), download('matricula', 'mapping_matricula')); module.exports = enrollmentApp; diff --git a/src/libs/routes/glossEnrollmentRatio.js b/src/libs/routes/glossEnrollmentRatio.js new file mode 100644 index 0000000000000000000000000000000000000000..0a929ca7727dca5fd5df654183f8d3dea6a96d31 --- /dev/null +++ b/src/libs/routes/glossEnrollmentRatio.js @@ -0,0 +1,365 @@ +const express = require('express'); + +const glossEnrollmentRatioApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const multiQuery = require(`${libs}/middlewares/multiQuery`); + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const download = require(`${libs}/middlewares/downloadDatabase`); + +const passport = require('passport'); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +glossEnrollmentRatioApp.use(cache('15 day')); + +// Complete range of the enrollments dataset. +// Returns a tuple of start and ending years of the complete enrollments dataset. +glossEnrollmentRatioApp.get('/year_range', (req, res, next) => { + req.sql.from('pnad') + .field('DISTINCT pnad.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('matricula') + .field('DISTINCT matricula.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let distinct_years = []; + let new_result = []; + for (let i = 0; i < req.oldResult.length; i++) { + for (let j = 0; j < req.result.length; j++) { + if(req.oldResult[i].year == req.result[j].year) { + distinct_years.push(req.oldResult[i]); + } + } + } + new_result.push({start_year: distinct_years[distinct_years.length -1].year, end_year: distinct_years[0].year}); + req.result = new_result; + next(); +}, response('range')); + +glossEnrollmentRatioApp.get('/years', (req, res, next) => { + req.sql.from('pnad') + .field('DISTINCT pnad.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('matricula') + .field('DISTINCT matricula.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let new_result = [] + for (let i = 0; i < req.oldResult.length; i++) { + for (let j = 0; j < req.result.length; j++) { + if(req.oldResult[i].year == req.result[j].year) { + new_result.push(req.oldResult[i]); + } + } + } + req.result = new_result; + next(); +}, response('years')); + +glossEnrollmentRatioApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'pnad\''); + next(); +}, query, response('source')); + +glossEnrollmentRatioApp.get('/education_level_basic', (req, res, next) => { + req.result = [ + {id: null, name: 'Não classificada'}, + {id: 1, name: 'Creche'}, + {id: 2, name: 'Pré-Escola'}, + {id: 4, name: 'Ensino Fundamental - anos iniciais'}, + {id: 5, name: 'Ensino Fundamental - anos finais'}, + {id: 6, name: 'Ensino Médio'} + ]; + next(); +}, response('education_level_basic')); + +glossEnrollmentRatioApp.get('/gender', (req, res, next) => { + req.result = [ + {id: 1, name: 'Masculino'}, + {id: 2, name: 'Feminino'} + ]; + next(); +}, response('gender')); + +glossEnrollmentRatioApp.get('/ethnic_group', (req, res, next) => { + req.result = [ + {id: 0, name: 'Sem declaração'}, + {id: 1, name: 'Branca'}, + {id: 2, name: 'Preta'}, + {id: 3, name: 'Parda'}, + {id: 4, name: 'Amarela'}, + {id: 5, name: 'Indígena'} + ]; + next(); +}, response('ethnic_group')); + +glossEnrollmentRatioApp.get('/location', (req, res, next) => { + req.result = [ + {id: 1, name: 'Urbana'}, + {id: 2, name: 'Rural'} + ]; + next(); +}, response('location')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: '@' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: '@' + } +}).addValue({ + name: 'ethnic_group', + table: '@', + tableField: 'cor_raca_id', + resultField: 'ethnic_group_id', + where: { + relation: '=', + type: 'integer', + field: 'cor_raca_id' + } +}).addValue({ + name: 'min_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'gender', + table: '@', + tableField: 'sexo', + resultField: 'gender_id', + where: { + relation: '=', + type: 'integer', + field: 'sexo' + } +}).addValue({ + name: 'location', + table: '@', + tableField: 'localizacao_id', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'localizacao_id' + } +}).addValue({ + name: 'education_level_basic', + table: 'matricula', + tableField: 'etapas_mod_ensino_segmento_id', + resultField: 'education_level_basic_id', + where: { + relation: '=', + type: 'integer', + field: 'etapas_mod_ensino_segmento_id' + } +}); + +function matchQueries(queryTotal, queryPartial) { + let match = []; + queryPartial.forEach((result) => { + let newObj = {}; + let keys = Object.keys(result); + keys.forEach((key) => { + newObj[key] = result[key]; + }); + // console.log('NEW OBJ'); + // console.log(newObj); + // remove total + let index = keys.indexOf('total'); + if(index > -1) keys.splice(index, 1); + // remove education_level_basic_id + index = keys.indexOf('education_level_basic_id'); + if(index > -1) keys.splice(index, 1); + // remove education_level_basic_name + index = keys.indexOf('education_level_basic_name'); + if(index > -1) keys.splice(index, 1); + let objMatch = null; + + for(let i = 0; i < queryTotal.length; ++i) { + let total = queryTotal[i]; + let foundMatch = true; + for(let j = 0; j < keys.length; ++j) { + let key = keys[j]; + if(total[key] !== result[key]) { + foundMatch = false; + break; + } + } + if(foundMatch) { + objMatch = total; + break; + } + } + + if(objMatch) { + // console.log('MATCH!!!!'); + // console.log(objMatch); + newObj.total = (result.total / objMatch.total) * 100; + newObj.partial = result.total; + newObj.denominator = objMatch.total + match.push(newObj); + } + }); + // console.log('TAMANHOS'); + // console.log(queryTotal.length); + // console.log(queryPartial.length); + // console.log(match.length); + return match; +} + +glossEnrollmentRatioApp.get('/', rqf.parse(),(req, res, next) => { + req.numerator = {}; + req.denominator = {}; + let glossEnrollmentRatioApp = {}; + + req.sql.from('matricula') + .field('count(*)', 'total') + .field('matricula.ano_censo', 'year') + .group('matricula.ano_censo') + .order('matricula.ano_censo') + + next(); +}, rqf.build(), query, id2str.transform(), (req, res, next) => { + req.numerator = req.result; + req.resetSql(); + req.sql.field('sum(peso)', 'total') + .field('pnad.ano_censo','year') + .from('pnad') + .group('pnad.ano_censo') + .order('pnad.ano_censo') + + function convert(result) { + if (result == 1) { + return 'pnad.faixa_etaria_31_03 = 1' + } else if (result == 2) { + return 'pnad.faixa_etaria_31_03 = 2' + } else if (result == 4) { + return 'pnad.faixa_etaria_31_03 = 3' + } else if (result == 5) { + return 'pnad.faixa_etaria_31_03 = 4' + } else if (result == 6) { + return 'pnad.faixa_etaria_31_03 = 5' + } + } + + //remove education_level_basic how filter and add faixa_etaria_31_03 in filter + if ("education_level_basic" in req.filter) { + if (Array.isArray(req.filter.education_level_basic)) { + var string_query = ''; + for(let i = 0; i < req.filter.education_level_basic.length - 1; i++) { + string_query = string_query + convert(req.filter.education_level_basic[i]) + ' OR '; + } + string_query = string_query + convert(req.filter.education_level_basic[req.filter.education_level_basic.length - 1]); + req.sql.where(string_query); + } else { + req.sql.where(convert(req.filter.education_level_basic)); + } + } else { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } + + next(); +}, rqf.parse(), (req, res, next) => { + if ("education_level_basic" in req.filter) { + delete req.filter.education_level_basic; + } + if ("education_level_basic" in req.dims) { + delete req.dims.education_level_basic; + } + next(); +}, rqf.build(), query, id2str.transform(), (req, res, next) => { + req.denominator = req.result; + + req.result = [] + let glossEnrollment = matchQueries(req.denominator, req.numerator); + req.result = glossEnrollment; + + next(); +}, response('glossEnrollmentRatio')); + +module.exports = glossEnrollmentRatioApp; diff --git a/src/libs/routes/idhm.js b/src/libs/routes/idhm.js new file mode 100644 index 0000000000000000000000000000000000000000..45a193e7750aab100b417a25affae69b0678ea71 --- /dev/null +++ b/src/libs/routes/idhm.js @@ -0,0 +1,192 @@ +const express = require('express'); + +const idhmApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +idhmApp.use(cache('15 day')); + +idhmApp.get('/year_range', (req, res, next) => { + req.sql.from('adh_idh') + .field('MIN(adh_idh.ano_censo)', 'start_year') + .field('MAX(adh_idh.ano_censo)', 'end_year'); + next(); +}, query, (req, res, next) => { + req.sql.from('adh_idh_uf') + .field('MIN(adh_idh_uf.ano_censo)', 'start_year') + .field('MAX(adh_idh_uf.ano_censo)', 'end_year'); + req.old_result = req.result; + next(); +}, query, (req, res, next) => { + if (req.old_result[0].start_year < req.result[0].start_year) { + req.result[0].start_year = req.old_result[0].start_year; + } + if (req.old_result[0].end_year > req.result[0].end_year) { + req.result[0].end_year = req.old_result[0].old_result; + } + next(); +}, query, response('range')); + +idhmApp.get('/years', (req, res, next) => { + req.sql.from('adh_idh') + .field('DISTINCT adh_idh.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('adh_idh_uf') + .field('DISTINCT adh_idh_uf.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let result = Object.assign(req.oldResult, req.result); + req.result = result; + next(); +}, response('years')); + +idhmApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'adh_idh\''); + next(); +}, query, response('source')); + +idhmApp.get('/IDHM_level', (req, res, next) => { + req.result = [ + {id: null, name: 'Não classificada'}, + {id: 1, name: 'Muito Baixa'}, + {id: 2, name: 'Baixo'}, + {id: 3, name: 'Médio'}, + {id: 4, name: 'Alto'}, + {id: 5, name: 'Muito Alto'} + ]; + next(); +}, response('IDHM_level')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'adh_idh' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'adh_idh' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: '@' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: '@' + } +}).addValue({ + name: 'min_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'idhm_level', + table: '@', + tableField: 'idhm_nivel', + resultField: 'idhm_level_id', + where: { + relation: '=', + type: 'integer', + table: '@', + field: 'idhm_nivel' + } +}); + + +idhmApp.get('/', rqf.parse(), (req, res, next) => { + if (("city" in req.dims) || ("city" in req.filter)) { + req.sql.from('adh_idh') + .field('adh_idh.idhm', 'total') + .field('adh_idh.ano_censo', 'year') + .field('adh_idh.municipio_id', 'city_id') + .field('adh_idh.estado_id', 'state_id') + .group('adh_idh.idhm') + .group('adh_idh.ano_censo') + .group('adh_idh.municipio_id') + .group('adh_idh.estado_id') + } else if (("state" in req.filter) || ("state" in req.dims)) { + req.sql.from('adh_idh_uf') + .field('adh_idh_uf.idhm', 'total') + .field('adh_idh_uf.ano_censo', 'year') + .field('adh_idh_uf.estado_id', 'state_id') + .group('adh_idh_uf.idhm') + .group('adh_idh_uf.ano_censo') + .group('adh_idh_uf.estado_id') + } else { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } + next(); +}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('idhm')); + +module.exports = idhmApp; diff --git a/src/libs/routes/idhme.js b/src/libs/routes/idhme.js new file mode 100644 index 0000000000000000000000000000000000000000..2a1f4d033fc61b8f96a50d4a42001fca4da52b60 --- /dev/null +++ b/src/libs/routes/idhme.js @@ -0,0 +1,165 @@ +const express = require('express'); + +const idhmeApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +idhmeApp.use(cache('15 day')); + +idhmeApp.get('/year_range', (req, res, next) => { + req.sql.from('adh_idh') + .field('MIN(adh_idh.ano_censo)', 'start_year') + .field('MAX(adh_idh.ano_censo)', 'end_year'); + next(); +}, query, (req, res, next) => { + req.sql.from('adh_idh_uf') + .field('MIN(adh_idh_uf.ano_censo)', 'start_year') + .field('MAX(adh_idh_uf.ano_censo)', 'end_year'); + req.old_result = req.result; + next(); +}, query, (req, res, next) => { + if (req.old_result[0].start_year < req.result[0].start_year) { + req.result[0].start_year = req.old_result[0].start_year; + } + if (req.old_result[0].end_year > req.result[0].end_year) { + req.result[0].end_year = req.old_result[0].old_result; + } + next(); +}, query, response('range')); + +idhmeApp.get('/years', (req, res, next) => { + req.sql.from('adh_idh') + .field('DISTINCT adh_idh.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('adh_idh_uf') + .field('DISTINCT adh_idh_uf.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let result = Object.assign(req.oldResult, req.result); + req.result = result; + next(); +}, response('years')); + +idhmeApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'adh_idh\''); + next(); +}, query, response('source')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'adh_idh' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'adh_idh' + } +}).addValue({ + name: 'min_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: '@' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: '@' + } +}); + +idhmeApp.get('/', rqf.parse(), (req, res, next) => { + + if (("city" in req.dims) || ("city" in req.filter)) { + req.sql.from('adh_idh') + .field('adh_idh.idhm_e', 'total') + .field('adh_idh.ano_censo', 'year') + .field('adh_idh.municipio_id', 'city_id') + .group('adh_idh.idhm_e') + .group('adh_idh.ano_censo') + .group('adh_idh.municipio_id'); + } else if (("state" in req.filter) || ("state" in req.dims)) { + req.sql.from('adh_idh_uf') + .field('adh_idh_uf.idhm_e', 'total') + .field('adh_idh_uf.ano_censo', 'year') + .field('adh_idh_uf.estado_id', 'state_id') + .group('adh_idh_uf.idhm_e') + .group('adh_idh_uf.ano_censo') + .group('adh_idh_uf.estado_id'); + } else { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } + next(); +}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('idhme')); + +module.exports = idhmeApp; diff --git a/src/libs/routes/idhml.js b/src/libs/routes/idhml.js new file mode 100644 index 0000000000000000000000000000000000000000..1fddee79146257c2f837a937bb5864fcdbab20af --- /dev/null +++ b/src/libs/routes/idhml.js @@ -0,0 +1,165 @@ +const express = require('express'); + +const idhmlApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +idhmlApp.use(cache('15 day')); + +idhmlApp.get('/year_range', (req, res, next) => { + req.sql.from('adh_idh') + .field('MIN(adh_idh.ano_censo)', 'start_year') + .field('MAX(adh_idh.ano_censo)', 'end_year'); + next(); +}, query, (req, res, next) => { + req.sql.from('adh_idh_uf') + .field('MIN(adh_idh_uf.ano_censo)', 'start_year') + .field('MAX(adh_idh_uf.ano_censo)', 'end_year'); + req.old_result = req.result; + next(); +}, query, (req, res, next) => { + if (req.old_result[0].start_year < req.result[0].start_year) { + req.result[0].start_year = req.old_result[0].start_year; + } + if (req.old_result[0].end_year > req.result[0].end_year) { + req.result[0].end_year = req.old_result[0].old_result; + } + next(); +}, query, response('range')); + +idhmlApp.get('/years', (req, res, next) => { + req.sql.from('adh_idh') + .field('DISTINCT adh_idh.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('adh_idh_uf') + .field('DISTINCT adh_idh_uf.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let result = Object.assign(req.oldResult, req.result); + req.result = result; + next(); +}, response('years')); + +idhmlApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'adh_idh\''); + next(); +}, query, response('source')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'adh_idh' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'adh_idh' + } +}).addValue({ + name: 'min_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: '@' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: '@' + } +}); + +idhmlApp.get('/', rqf.parse(), (req, res, next) => { + + if (("city" in req.dims) || ("city" in req.filter)) { + req.sql.from('adh_idh') + .field('adh_idh.idhm_l', 'total') + .field('adh_idh.ano_censo', 'year') + .field('adh_idh.municipio_id', 'city_id') + .group('adh_idh.idhm_l') + .group('adh_idh.ano_censo') + .group('adh_idh.municipio_id'); + } else if (("state" in req.filter) || ("state" in req.dims)) { + req.sql.from('adh_idh_uf') + .field('adh_idh_uf.idhm_l', 'total') + .field('adh_idh_uf.ano_censo', 'year') + .field('adh_idh_uf.estado_id', 'state_id') + .group('adh_idh_uf.idhm_l') + .group('adh_idh_uf.ano_censo') + .group('adh_idh_uf.estado_id'); + } else { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } + next(); +}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('idhme')); + +module.exports = idhmlApp; diff --git a/src/libs/routes/idhmr.js b/src/libs/routes/idhmr.js new file mode 100644 index 0000000000000000000000000000000000000000..938d58a93db62f00185c08a22a75f34403541c27 --- /dev/null +++ b/src/libs/routes/idhmr.js @@ -0,0 +1,168 @@ +const express = require('express'); + +const idhmrApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +idhmrApp.use(cache('15 day')); + +idhmrApp.get('/year_range', (req, res, next) => { + req.sql.from('adh_idh') + .field('MIN(adh_idh.ano_censo)', 'start_year') + .field('MAX(adh_idh.ano_censo)', 'end_year'); + next(); +}, query, (req, res, next) => { + req.sql.from('adh_idh_uf') + .field('MIN(adh_idh_uf.ano_censo)', 'start_year') + .field('MAX(adh_idh_uf.ano_censo)', 'end_year'); + req.old_result = req.result; + next(); +}, query, (req, res, next) => { + if (req.old_result[0].start_year < req.result[0].start_year) { + req.result[0].start_year = req.old_result[0].start_year; + } + if (req.old_result[0].end_year > req.result[0].end_year) { + req.result[0].end_year = req.old_result[0].old_result; + } + next(); +}, query, response('range')); + +idhmrApp.get('/years', (req, res, next) => { + req.sql.from('adh_idh') + .field('DISTINCT adh_idh.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('adh_idh_uf') + .field('DISTINCT adh_idh_uf.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let result = Object.assign(req.oldResult, req.result); + req.result = result; + next(); +}, response('years')); + +idhmrApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'adh_idh\''); + next(); +}, query, response('source')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'adh_idh' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'adh_idh' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: '@' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: '@' + } +}).addValue({ + name: 'min_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}); + +idhmrApp.get('/', rqf.parse(), (req, res, next) => { + if (("city" in req.dims) || ("city" in req.filter)) { + req.sql.from('adh_idh') + .field('adh_idh.idhm_r', 'total') + .field('adh_idh.ano_censo', 'year') + .field('adh_idh.municipio_id', 'city_id') + .field('adh_idh.estado_id', 'state_id') + .group('adh_idh.idhm_r') + .group('adh_idh.ano_censo') + .group('adh_idh.municipio_id') + .group('adh_idh.estado_id') + } else if (("state" in req.filter) || ("state" in req.dims)) { + req.sql.from('adh_idh_uf') + .field('adh_idh_uf.idhm_r', 'total') + .field('adh_idh_uf.ano_censo', 'year') + .field('adh_idh_uf.estado_id', 'state_id') + .group('adh_idh_uf.idhm_r') + .group('adh_idh_uf.ano_censo') + .group('adh_idh_uf.estado_id') + } else { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } + next(); +}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('idhmr')); + +module.exports = idhmrApp; diff --git a/src/libs/routes/infrastructure.js b/src/libs/routes/infrastructure.js new file mode 100644 index 0000000000000000000000000000000000000000..e201e1f41f4156926c04fa76fa78895409507b36 --- /dev/null +++ b/src/libs/routes/infrastructure.js @@ -0,0 +1,542 @@ +const express = require('express'); + +const infrastructureApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const multiQuery = require(`${libs}/middlewares/multiQuery`); + +const response = require(`${libs}/middlewares/response`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +infrastructureApp.use(cache('15 day')); + +infrastructureApp.get('/year_range', (req, res, next) => { + req.sql.from('escola') + .field('MIN(escola.ano_censo)', 'start_year') + .field('MAX(escola.ano_censo)', 'end_year'); + next(); +}, query, response('range')); + +infrastructureApp.get('/years', (req, res, next) => { + req.sql.from('escola') + .field('DISTINCT escola.ano_censo', 'year'); + next(); +}, query, response('years')); + +infrastructureApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'escola\''); + next(); +}, query, response('source')); + +infrastructureApp.get('/location', (req, res, next) => { + req.result = [ + {id: 1, name: 'Urbana'}, + {id: 2, name: 'Rural'} + ]; + next(); +}, response('location')); + +infrastructureApp.get('/rural_location', (req, res, next) => { + req.result = [ + {id: 1, name: "Urbana"}, + {id: 2, name: "Rural"}, + {id: 3, name: "Rural - Área de assentamento"}, + {id: 4, name: "Rural - Terra indígena"}, + {id: 5, name: "Rural - Área remanescente de quilombos"}, + {id: 6, name: "Rural - Unidade de uso sustentável"} + ]; + next(); +}, response('rural_location')); + +infrastructureApp.get('/adm_dependency', (req, res, next) => { + req.sql.from('dependencia_adm') + .field('id') + .field('nome', 'name') + .where('id <= 4'); + next(); +}, query, response('adm_dependency')); + +infrastructureApp.get('/adm_dependency_detailed', (req, res, next) => { + req.sql.from('dependencia_adm_priv') + .field('id', 'id') + .field('nome', 'name'); + next(); +}, query, response('adm_dependency_detailed')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValueToField({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'escola' + } +}, 'dims').addValueToField({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'escola' + } +}, 'filter').addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'escola' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'escola' + } +}).addValue({ + name: 'location', + table: 'escola', + tableField: 'cod_localizacao', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'cod_localizacao' + } +}).addValue({ + name: 'rural_location', + table: 'escola', + tableField: 'localidade_area_rural', + resultField: 'rural_location_id', + where: { + relation: '=', + type: 'integer', + field: 'localidade_area_rural' + } +}).addValue({ + name: 'adm_dependency', + table: 'escola', + tableField: 'dependencia_adm_id', + resultField: 'adm_dependency_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_id' + } +}).addValue({ + name: 'adm_dependency_detailed', + table: 'escola', + tableField: 'dependencia_adm_priv', + resultField: 'adm_dependency_detailed_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_priv' + } +}).addValue({ + name: 'min_year', + table: 'escola', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: 'escola', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + field: 'ano_censo' + } +}); + +function matchQueries(queryTotal, queryPartial) { + let match = []; + queryTotal.forEach((result) => { + let newObj = {}; + let keys = Object.keys(result); + keys.forEach((key) => { + newObj[key] = result[key]; + }); + let index = keys.indexOf('total'); + if(index > -1) keys.splice(index, 1); + let objMatch = null; + + for(let i = 0; i < queryPartial.length; ++i) { + let partial = queryPartial[i]; + let foundMatch = true; + for(let j = 0; j < keys.length; ++j) { + let key = keys[j]; + if(partial[key] !== result[key]) { + foundMatch = false; + break; + } + } + if(foundMatch) { + objMatch = partial; + break; + } + } + + if(objMatch) { + newObj.percentage = (objMatch.total / result.total) * 100; + newObj.partial = objMatch.total; + newObj.total = result.total + match.push(newObj); + } + }); + + return match; +} + +infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + req.querySet = []; + req.queryIndex = {}; + + // Local de funcionamento + let allSchools = req.sql.clone(); + allSchools.from('escola').field('COUNT(escola.id)', 'total') + .field("'Brasil'", 'name') + .field('escola.ano_censo', 'year') + .group('escola.ano_censo') + .where('escola.situacao_de_funcionamento = 1') + .order('escola.ano_censo'); + req.queryIndex.allSchools = req.querySet.push(allSchools) - 1; + + let schoolPlace = allSchools.clone(); + schoolPlace.where('escola.func_predio_escolar = 1 AND escola.func_salas_empresa = 0 AND escola.func_templo_igreja = 0 AND escola.func_casa_professor = 0 AND escola.func_galpao = 0 AND escola.biblioteca = 1'); + req.queryIndex.schoolPlace = req.querySet.push(schoolPlace) - 1; + + // Bibliotecas + let allLibraries = allSchools.clone(); + allLibraries.where('escola.func_predio_escolar = 1 AND escola.cod_localizacao = 1'); + req.queryIndex.allLibraries = req.querySet.push(allLibraries) - 1; + + let haveLibraries = allLibraries.clone(); + haveLibraries.where('escola.biblioteca = 1'); + req.queryIndex.haveLibraries = req.querySet.push(haveLibraries) - 1; + + // Bibliotecas/Sala de leitura + let allLibrariesReadingRoom = allSchools.clone(); + allLibrariesReadingRoom.where('escola.func_predio_escolar = 1 AND escola.cod_localizacao = 2'); + req.queryIndex.allLibrariesReadingRoom = req.querySet.push(allLibrariesReadingRoom) - 1; + + let haveLibrariesReadingRoom = allLibrariesReadingRoom.clone(); + haveLibrariesReadingRoom.where('escola.sala_leitura = 1'); + req.queryIndex.haveLibrariesReadingRoom = req.querySet.push(haveLibrariesReadingRoom) - 1; + + // Laboratório de informática + let allInfLab = allSchools.clone(); + allInfLab.where('escola.func_predio_escolar = 1') + .where('escola.reg_fund_ai = 1 OR escola.reg_fund_af = 1 OR escola.reg_medio_medio = 1 OR escola.reg_medio_integrado = 1 OR escola.reg_medio_normal = 1 OR escola.ensino_eja_fund = 1 OR escola.ensino_eja_medio = 1 OR escola.ensino_eja_prof = 1'); + req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1; + + let haveInfLab = allInfLab.clone(); + haveInfLab.where('escola.lab_informatica = 1'); + req.queryIndex.haveInfLab = req.querySet.push(haveInfLab) - 1; + + // Laboratório de ciências + let allScienceLab = allInfLab.clone(); + req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1; + + let haveScienceLab = allScienceLab.clone(); + haveScienceLab.where('escola.lab_ciencias = 1'); + req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1; + + // Parque infantil + let allKidsPark = allSchools.clone(); + allKidsPark.where('escola.func_predio_escolar = 1') + .where('escola.reg_infantil_creche = 1 OR escola.reg_infantil_preescola = 1 OR escola.reg_fund_ai = 1 OR escola.esp_infantil_creche = 1 OR escola.esp_exclusiva_creche = 1 OR escola.reg_esp_exclusiva_fund_ai = 1'); + req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1; + + let haveKidsPark = allKidsPark.clone(); + haveKidsPark.where('escola.parque_infantil = 1'); + req.queryIndex.haveKidsPark = req.querySet.push(haveKidsPark) - 1; + + // Berçário + let allCribs = allSchools.clone(); + allCribs.where('escola.func_predio_escolar = 1') + .where('escola.reg_infantil_creche = 1 OR escola.esp_infantil_creche = 1'); + req.queryIndex.allCribs = req.querySet.push(allCribs) - 1; + + let haveCribs = allCribs.clone(); + haveCribs.where('escola.bercario = 1'); + req.queryIndex.haveCribs = req.querySet.push(haveCribs) - 1; + + // Quadra + let allSportsCourt = allScienceLab.clone(); + allSportsCourt.where('escola.cod_localizacao = 1'); + req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1; + + let haveSportsCourt = allSportsCourt.clone(); + haveSportsCourt.where('escola.quadra_esportes = 1'); + req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1; + + // Quadra coberta + req.queryIndex.allCoveredSportsCourt = req.queryIndex.allSportsCourt; + + let haveCoveredSportsCourt = allSportsCourt.clone(); + haveCoveredSportsCourt.where('escola.quadra_esportes_coberta = 1'); + req.queryIndex.haveCoveredSportsCourt = req.querySet.push(haveCoveredSportsCourt) - 1; + + // Quadra Descoberta + let allUncoveredSportsCourt = allSportsCourt.clone(); + allUncoveredSportsCourt.where('escola.quadra_esportes_coberta = 0'); + req.queryIndex.allUncoveredSportsCourt = req.querySet.push(allUncoveredSportsCourt) - 1; + + let haveUncoveredSportsCourt = allUncoveredSportsCourt.clone(); + haveUncoveredSportsCourt.where('escola.quadra_esportes_descoberta = 1'); + req.queryIndex.haveUncoveredSportsCourt = req.querySet.push(haveUncoveredSportsCourt) - 1; + + // Sala de direção + let allDirectorRoom = allSchools.clone(); + allDirectorRoom.where('escola.func_predio_escolar = 1 AND escola.cod_localizacao = 1'); + req.queryIndex.allDirectorRoom = req.querySet.push(allDirectorRoom) - 1; + + let haveDirectorRoom = allDirectorRoom.clone(); + haveDirectorRoom.where('escola.sala_diretoria = 1'); + req.queryIndex.haveDirectorRoom = req.querySet.push(haveDirectorRoom) - 1; + + // Secretaria + let allSecretary = allSchools.clone(); + allSecretary.where('escola.func_predio_escolar = 1'); + req.queryIndex.allSecretary = req.querySet.push(allSecretary) - 1; + + let haveSecretary = allSecretary.clone(); + haveSecretary.where('escola.secretaria = 1'); + req.queryIndex.haveSecretary = req.querySet.push(haveSecretary) - 1; + + // Sala de professores + req.queryIndex.allTeacherRoom = req.queryIndex.allSecretary; + + let haveTeacherRoom = allSecretary.clone(); + haveTeacherRoom.where('escola.sala_professor = 1'); + req.queryIndex.haveTeacherRoom = req.querySet.push(haveTeacherRoom) - 1; + + // Cozinha + req.queryIndex.allKitchen = req.queryIndex.allSecretary; + + let haveKitchen = allSecretary.clone(); + haveKitchen.where('escola.cozinha = 1'); + req.queryIndex.haveKitchen = req.querySet.push(haveKitchen) - 1; + + // Despensa + req.queryIndex.allStoreroom = req.queryIndex.allSecretary; + + let haveStoreroom = allSecretary.clone(); + haveStoreroom.where('escola.despensa = 1'); + req.queryIndex.haveStoreroom = req.querySet.push(haveStoreroom) - 1; + + // Almoxarifado + req.queryIndex.allWarehouse = req.queryIndex.allSecretary; + + let haveWarehouse = allSecretary.clone(); + haveWarehouse.where('escola.almoxarifado = 1'); + req.queryIndex.haveWarehouse = req.querySet.push(haveWarehouse) - 1; + + // Internet + req.queryIndex.allInternet = req.queryIndex.allLibrariesReadingRoom; + + let haveInternet = allLibrariesReadingRoom.clone(); + haveInternet.where('escola.internet = 1'); + req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1; + + // Internet banda larga + req.queryIndex.allBroadbandInternet = req.queryIndex.allLibraries; + + let haveBroadbandInternet = allLibraries.clone(); + haveBroadbandInternet.where('escola.internet_banda_larga = 1'); + req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1; + + // Banheiro dentro do prédio + req.queryIndex.allInsideBathroom = req.queryIndex.allSecretary; + + let haveInsideBathroom = allSecretary.clone(); + haveInsideBathroom.where('escola.sanitario_dentro_predio = 1'); + req.queryIndex.haveInsideBathroom = req.querySet.push(haveInsideBathroom) - 1; + + // Banheiro adequado para educação infantil dentro do prédio + req.queryIndex.allInsideKidsBathroom = req.queryIndex.allKidsPark; + + let haveInsideKidsBathroom = allKidsPark.clone(); + haveInsideKidsBathroom.where('escola.sanitario_ei = 1'); + req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1; + + // Fornecimento de energia + req.queryIndex.allEletricEnergy = req.queryIndex.allSecretary; + + let haveEletricEnergy = allSecretary.clone(); + haveEletricEnergy.where('escola.fornecimento_energia = 1'); + req.queryIndex.haveEletricEnergy = req.querySet.push(haveEletricEnergy) - 1; + + // Abastecimento de água + req.queryIndex.allWaterSupply = req.queryIndex.allSecretary; + + let haveWaterSupply = allSecretary.clone(); + haveWaterSupply.where('escola.fornecimento_agua = 1'); + req.queryIndex.haveWaterSupply = req.querySet.push(haveWaterSupply) - 1; + + // Água filtrada + req.queryIndex.allFilteredWater = req.queryIndex.allSecretary; + + let haveFilteredWater = allSecretary.clone(); + haveFilteredWater.where('escola.agua_filtrada = 1'); + req.queryIndex.haveFilteredWater = req.querySet.push(haveFilteredWater) - 1; + + // Coleta de esgoto + req.queryIndex.allSewage = req.queryIndex.allSecretary; + + let haveSewage = allSecretary.clone(); + haveSewage.where('escola.esgoto_sanitario = 1'); + req.queryIndex.haveSewage = req.querySet.push(haveSewage) - 1; + + // Sala de recursos multifuncionais para Atendimento Educacional Especializado + req.queryIndex.allMultifunctionRoom = req.queryIndex.allSecretary; + + let haveMultifunctionRoom = allSecretary.clone(); + haveMultifunctionRoom.where('escola.sala_atendimento_especial = 1'); + req.queryIndex.haveMultifunctionRoom = req.querySet.push(haveMultifunctionRoom) - 1; + + // Banheiros adaptados para pessoas com deficiências + req.queryIndex.allSpecialBathroom = req.queryIndex.allSecretary; + + let haveSpecialBathroom = allSecretary.clone(); + haveSpecialBathroom.where('escola.sanitario_pne = 1'); + req.queryIndex.haveSpecialBathroom = req.querySet.push(haveSpecialBathroom) - 1; + + // Dependências adaptada para pessoas com deficiências + req.queryIndex.allAdaptedBuilding = req.queryIndex.allSecretary; + + let haveAdaptedBuilding = allSecretary.clone(); + haveAdaptedBuilding.where('escola.dependencias_pne = 1'); + req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1; + + next(); +}, multiQuery, (req, res, next) => { + // Faz o matching entre os resultados + let school_place = matchQueries(req.result[req.queryIndex.allSchools], req.result[req.queryIndex.schoolPlace]); + let libraries = matchQueries(req.result[req.queryIndex.allLibraries], req.result[req.queryIndex.haveLibraries]); + let libraries_reading_room = matchQueries(req.result[req.queryIndex.allLibrariesReadingRoom], req.result[req.queryIndex.haveLibrariesReadingRoom]); + let computer_lab = matchQueries(req.result[req.queryIndex.allInfLab], req.result[req.queryIndex.haveInfLab]); + let science_lab = matchQueries(req.result[req.queryIndex.allScienceLab], req.result[req.queryIndex.haveScienceLab]); + let kids_park = matchQueries(req.result[req.queryIndex.allKidsPark], req.result[req.queryIndex.haveKidsPark]); + let nursery = matchQueries(req.result[req.queryIndex.allCribs], req.result[req.queryIndex.haveCribs]); + let sports_court = matchQueries(req.result[req.queryIndex.allSportsCourt], req.result[req.queryIndex.haveSportsCourt]); + let covered_sports_court = matchQueries(req.result[req.queryIndex.allCoveredSportsCourt], req.result[req.queryIndex.haveCoveredSportsCourt]); + let uncovered_sports_court = matchQueries(req.result[req.queryIndex.allUncoveredSportsCourt], req.result[req.queryIndex.haveUncoveredSportsCourt]); + let director_room = matchQueries(req.result[req.queryIndex.allDirectorRoom], req.result[req.queryIndex.haveDirectorRoom]); + let secretary = matchQueries(req.result[req.queryIndex.allSecretary], req.result[req.queryIndex.haveSecretary]); + let teacher_room = matchQueries(req.result[req.queryIndex.allTeacherRoom], req.result[req.queryIndex.haveTeacherRoom]); + let kitchen = matchQueries(req.result[req.queryIndex.allKitchen], req.result[req.queryIndex.haveKitchen]); + let storeroom = matchQueries(req.result[req.queryIndex.allStoreroom], req.result[req.queryIndex.haveStoreroom]); + let warehouse = matchQueries(req.result[req.queryIndex.allWarehouse], req.result[req.queryIndex.haveWarehouse]); + let internet = matchQueries(req.result[req.queryIndex.allInternet], req.result[req.queryIndex.haveInternet]); + let broadband_internet = matchQueries(req.result[req.queryIndex.allBroadbandInternet], req.result[req.queryIndex.haveBroadbandInternet]); + let inside_bathroom = matchQueries(req.result[req.queryIndex.allInsideBathroom], req.result[req.queryIndex.haveInsideBathroom]); + let inside_kids_bathroom = matchQueries(req.result[req.queryIndex.allInsideKidsBathroom], req.result[req.queryIndex.haveInsideKidsBathroom]); + let eletric_energy = matchQueries(req.result[req.queryIndex.allEletricEnergy], req.result[req.queryIndex.haveEletricEnergy]); + let water_supply = matchQueries(req.result[req.queryIndex.allWaterSupply], req.result[req.queryIndex.haveWaterSupply]); + let filtered_water = matchQueries(req.result[req.queryIndex.allFilteredWater], req.result[req.queryIndex.haveFilteredWater]); + let sewage_treatment = matchQueries(req.result[req.queryIndex.allSewage], req.result[req.queryIndex.haveSewage]); + let special_multifunction_room = matchQueries(req.result[req.queryIndex.allMultifunctionRoom], req.result[req.queryIndex.haveMultifunctionRoom]); + let special_bathroom = matchQueries(req.result[req.queryIndex.allSpecialBathroom], req.result[req.queryIndex.haveSpecialBathroom]); + let adapted_building = matchQueries(req.result[req.queryIndex.allAdaptedBuilding], req.result[req.queryIndex.haveAdaptedBuilding]); + + req.result = [{ + school_place, + libraries, + libraries_reading_room, + computer_lab, + science_lab, + kids_park, + nursery, + sports_court, + covered_sports_court, + uncovered_sports_court, + director_room, + secretary, + teacher_room, + kitchen, + storeroom, + warehouse, + internet, + broadband_internet, + inside_bathroom, + inside_kids_bathroom, + eletric_energy, + water_supply, + filtered_water, + sewage_treatment, + special_multifunction_room, + special_bathroom, + adapted_building + }]; + + next(); +}, id2str.multitransform(false), response('infrastructure')); + +module.exports = infrastructureApp; diff --git a/src/libs/routes/liquidEnrollmentRatio.js b/src/libs/routes/liquidEnrollmentRatio.js new file mode 100644 index 0000000000000000000000000000000000000000..1632e06f465152bf235466d3b38893265f7c3eb3 --- /dev/null +++ b/src/libs/routes/liquidEnrollmentRatio.js @@ -0,0 +1,392 @@ +const express = require('express'); + +const liquidEnrollmentRatioApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const multiQuery = require(`${libs}/middlewares/multiQuery`); + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const download = require(`${libs}/middlewares/downloadDatabase`); + +const passport = require('passport'); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +liquidEnrollmentRatioApp.use(cache('15 day')); + +// Complete range of the enrollments dataset. +// Returns a tuple of start and ending years of the complete enrollments dataset. +liquidEnrollmentRatioApp.get('/year_range', (req, res, next) => { + req.sql.from('pnad') + .field('DISTINCT pnad.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('matricula') + .field('DISTINCT matricula.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let distinct_years = []; + let new_result = []; + for (let i = 0; i < req.oldResult.length; i++) { + for (let j = 0; j < req.result.length; j++) { + if(req.oldResult[i].year == req.result[j].year) { + distinct_years.push(req.oldResult[i]); + } + } + } + new_result.push({start_year: distinct_years[distinct_years.length -1].year, end_year: distinct_years[0].year}); + req.result = new_result; + next(); +}, response('range')); + +liquidEnrollmentRatioApp.get('/years', (req, res, next) => { + req.sql.from('pnad') + .field('DISTINCT pnad.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('matricula') + .field('DISTINCT matricula.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let new_result = [] + for (let i = 0; i < req.oldResult.length; i++) { + for (let j = 0; j < req.result.length; j++) { + if(req.oldResult[i].year == req.result[j].year) { + new_result.push(req.oldResult[i]); + } + } + } + req.result = new_result; + next(); +}, response('years')); + +liquidEnrollmentRatioApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'pnad\''); + next(); +}, query, response('source')); + +liquidEnrollmentRatioApp.get('/education_level_basic', (req, res, next) => { + req.result = [ + {id: null, name: 'Não classificada'}, + {id: 1, name: 'Creche'}, + {id: 2, name: 'Pré-Escola'}, + {id: 4, name: 'Ensino Fundamental - anos iniciais'}, + {id: 5, name: 'Ensino Fundamental - anos finais'}, + {id: 6, name: 'Ensino Médio'} + ]; + next(); +}, response('education_level_basic')); + +liquidEnrollmentRatioApp.get('/gender', (req, res, next) => { + req.result = [ + {id: 1, name: 'Masculino'}, + {id: 2, name: 'Feminino'} + ]; + next(); +}, response('gender')); + +liquidEnrollmentRatioApp.get('/ethnic_group', (req, res, next) => { + req.result = [ + {id: 0, name: 'Sem declaração'}, + {id: 1, name: 'Branca'}, + {id: 2, name: 'Preta'}, + {id: 3, name: 'Parda'}, + {id: 4, name: 'Amarela'}, + {id: 5, name: 'Indígena'} + ]; + next(); +}, response('ethnic_group')); + +liquidEnrollmentRatioApp.get('/location', (req, res, next) => { + req.result = [ + {id: 1, name: 'Urbana'}, + {id: 2, name: 'Rural'} + ]; + next(); +}, response('location')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: '@' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: '@' + } +}).addValue({ + name: 'ethnic_group', + table: '@', + tableField: 'cor_raca_id', + resultField: 'ethnic_group_id', + where: { + relation: '=', + type: 'integer', + field: 'cor_raca_id' + } +}).addValue({ + name: 'min_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'gender', + table: '@', + tableField: 'sexo', + resultField: 'gender_id', + where: { + relation: '=', + type: 'integer', + field: 'sexo' + } +}).addValue({ + name: 'location', + table: '@', + tableField: 'localizacao_id', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'localizacao_id' + } +}).addValue({ + name: 'education_level_basic', + table: 'matricula', + tableField: 'etapas_mod_ensino_segmento_id', + resultField: 'education_level_basic_id', + where: { + relation: '=', + type: 'integer', + field: 'etapas_mod_ensino_segmento_id' + } +}); + +function matchQueries(queryTotal, queryPartial) { + let match = []; + queryPartial.forEach((result) => { + let newObj = {}; + let keys = Object.keys(result); + keys.forEach((key) => { + newObj[key] = result[key]; + }); + // console.log('NEW OBJ'); + // console.log(newObj); + // remove total + let index = keys.indexOf('total'); + if(index > -1) keys.splice(index, 1); + // remove education_level_basic_id + index = keys.indexOf('education_level_basic_id'); + if(index > -1) keys.splice(index, 1); + // remove education_level_basic_name + index = keys.indexOf('education_level_basic_name'); + if(index > -1) keys.splice(index, 1); + let objMatch = null; + + for(let i = 0; i < queryTotal.length; ++i) { + let total = queryTotal[i]; + let foundMatch = true; + for(let j = 0; j < keys.length; ++j) { + let key = keys[j]; + if(total[key] !== result[key]) { + foundMatch = false; + break; + } + } + if(foundMatch) { + objMatch = total; + break; + } + } + + if(objMatch) { + // console.log('MATCH!!!!'); + // console.log(objMatch); + newObj.total = (result.total / objMatch.total) * 100; + newObj.partial = result.total; + newObj.denominator = objMatch.total + match.push(newObj); + } + }); + // console.log('TAMANHOS'); + // console.log(queryTotal.length); + // console.log(queryPartial.length); + // console.log(match.length); + return match; +} + + +liquidEnrollmentRatioApp.get('/', rqf.parse(),(req, res, next) => { + req.numerator = {}; + req.denominator = {}; + let liquidEnrollmentRatioApp = {}; + + req.sql.from('matricula') + .field('count(*)', 'total') + .field('matricula.ano_censo', 'year') + .group('matricula.ano_censo') + .order('matricula.ano_censo') + + function ConvertMatricula(result) { + if (result == 1) { + return 'matricula.faixa_etaria_31_03 = 1' + } else if (result == 2) { + return 'matricula.faixa_etaria_31_03 = 2' + } else if (result == 4) { + return 'matricula.faixa_etaria_31_03 = 3' + } else if (result == 5) { + return 'matricula.faixa_etaria_31_03 = 4' + } else if (result == 6) { + return 'matricula.faixa_etaria_31_03 = 5' + } + } + if ("education_level_basic" in req.filter) { + if (Array.isArray(req.filter.education_level_basic)) { + var string_query_enrollment = ''; + for(let i = 0; i < req.filter.education_level_basic.length - 1; i++) { + string_query_enrollment = string_query_enrollment + ConvertMatricula(req.filter.education_level_basic[i]) + ' OR '; + } + string_query_enrollment = string_query_enrollment + ConvertMatricula(req.filter.education_level_basic[req.filter.education_level_basic.length - 1]); + req.sql.where(string_query_enrollment); + } else { + req.sql.where(ConvertMatricula(req.filter.education_level_basic)); + } + } else { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } + + next(); +}, rqf.build(), query, id2str.transform(), (req, res, next) => { + req.numerator = req.result; + req.resetSql(); + req.sql.field('sum(peso)', 'total') + .field('pnad.ano_censo','year') + .from('pnad') + .group('pnad.ano_censo') + .order('pnad.ano_censo') + + function convertPnad(result) { + if (result == 1) { + return 'pnad.faixa_etaria_31_03 = 1' + } else if (result == 2) { + return 'pnad.faixa_etaria_31_03 = 2' + } else if (result == 4) { + return 'pnad.faixa_etaria_31_03 = 3' + } else if (result == 5) { + return 'pnad.faixa_etaria_31_03 = 4' + } else if (result == 6) { + return 'pnad.faixa_etaria_31_03 = 5' + } + } + + //remove education_level_basic how filter and add faixa_etaria_31_03 in filter + if ("education_level_basic" in req.filter) { + if (Array.isArray(req.filter.education_level_basic)) { + var string_query = ''; + for(let i = 0; i < req.filter.education_level_basic.length - 1; i++) { + string_query = string_query + convertPnad(req.filter.education_level_basic[i]) + ' OR '; + } + string_query = string_query + convertPnad(req.filter.education_level_basic[req.filter.education_level_basic.length - 1]); + req.sql.where(string_query); + } else { + req.sql.where(convertPnad(req.filter.education_level_basic)); + } + } + + next(); +}, rqf.parse(), (req, res, next) => { + if ("education_level_basic" in req.filter) { + delete req.filter.education_level_basic; + } + if ("education_level_basic" in req.dims) { + delete req.dims.education_level_basic; + } + next(); +}, rqf.build(), query, id2str.transform(), (req, res, next) => { + req.denominator = req.result; + + //division to generate req.result final + req.result = [] + let liquidEnrollment = matchQueries(req.denominator, req.numerator); + req.result = liquidEnrollment; + next(); +}, response('liquidEnrollmentRatio')); + +module.exports = liquidEnrollmentRatioApp; diff --git a/src/libs/routes/pibpercapita.js b/src/libs/routes/pibpercapita.js new file mode 100644 index 0000000000000000000000000000000000000000..10e03b0fa17bb497fe55c1552696411aa1d78302 --- /dev/null +++ b/src/libs/routes/pibpercapita.js @@ -0,0 +1,183 @@ +const express = require('express'); + +const pibpercapitaApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +pibpercapitaApp.use(cache('15 day')); + +pibpercapitaApp.get('/year_range', (req, res, next) => { + req.sql.from('ibge_pib') + .field('MIN(ibge_pib.ano_censo)', 'start_year') + .field('MAX(ibge_pib.ano_censo)', 'end_year') + .where('ibge_pib.ano_censo > 2013'); + next(); +}, query, response('range')); + +pibpercapitaApp.get('/years', (req, res, next) => { + req.sql.from('ibge_pib'). + field('DISTINCT ibge_pib.ano_censo', 'year') + .where('ibge_pib.ano_censo > 2013'); + next(); +}, query, response('years')); + +pibpercapitaApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'ibge_pib\''); + next(); +}, query, response('source')); + +pibpercapitaApp.get('/income_level', (req, res, next) => { + req.result = [ + {id: 1, name: "1º quintil – 20% menores"}, + {id: 2, name: "2º quintil"}, + {id: 3, name: "3º quintil"}, + {id: 4, name: "4º quintil"}, + {id: 5, name: "5º quintil – 20% maiores"}, + ]; + next(); +}, response('income_level')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'ibge_pib' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'ibge_pib' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'ibge_pib' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'ibge_pib' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'regiao_id', + table: 'ibge_pib' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'ibge_pib' + } +}).addValue({ + name: 'min_year', + table: 'ibge_pib', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: 'ibge_pib', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'income_level', + table: 'ibge_pib', + tableField: 'nivel_renda_per_capita', + resultField: 'income_level_id', + where: { + relation: '=', + type: 'integer', + field: 'nivel_renda_per_capita' + } +}); + +pibpercapitaApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + if (("city" in req.dims) || ("city" in req.filter)) { + req.sql.from('ibge_pib') + .field('ibge_pib.pib_per_capita', 'total') + .field('ibge_pib.ano_censo', 'year') + .group('ibge_pib.ano_censo') + .group('ibge_pib.pib_per_capita') + .order('ibge_pib.ano_censo') + } else { + req.sql.from('ibge_pib') + .field('SUM(ibge_pib.pib)/SUM(ibge_pib.populacao)', 'total') + .field('ibge_pib.ano_censo', 'year') + .group('ibge_pib.ano_censo') + .order('ibge_pib.ano_censo') + } + next(); +}, query, addMissing(rqf), id2str.transform(false), (req, res, next) => { + req.result.forEach((i) => { + let value = i.total; + let res = value.toString().split("."); + //rounding decimal. + let decimal = Math.round(res[1].toString().substring(0,2) + (".") + res[1].toString().substring(2,3)); + //case 0 after comma + if (res[1].toString().substring(0,1) == 0) { + i.total = parseFloat(res[0] + "." + "0" + decimal); + } else { + i.total = parseFloat(res[0] + "." + decimal); + } + }); + next(); + }, response("pibpercapita")); + + +module.exports = pibpercapitaApp; diff --git a/src/libs/routes/population.js b/src/libs/routes/population.js new file mode 100644 index 0000000000000000000000000000000000000000..cd307cdbcbb70b797c76d92b3550a0e3d9306604 --- /dev/null +++ b/src/libs/routes/population.js @@ -0,0 +1,153 @@ +const express = require('express'); + +const populationApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +populationApp.use(cache('15 day')); + +populationApp.get('/year_range', (req, res, next) => { + req.sql.from('ibge_populacao') + .field('MIN(ibge_populacao.ano_censo)', 'start_year') + .field('MAX(ibge_populacao.ano_censo)', 'end_year'); + next(); +}, query, response('range')); + +populationApp.get('/years', (req, res, next) => { + req.sql.from('ibge_populacao'). + field('DISTINCT ibge_populacao.ano_censo', 'year'); + next(); +}, query, response('years')); + +populationApp.get('/city_size', (req, res, next) => { + req.result = [ + {id: 1, name: "até 5000"}, + {id: 2, name: "5001 - 10000"}, + {id: 3, name: "10001 - 20000"}, + {id: 4, name: "20001 - 50000"}, + {id: 5, name: "50001 - 100000"}, + {id: 6, name: "100001 - 500000"}, + {id: 7, name: "mais que 500000"} + ]; + next(); +}, response('city_size')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'ibge_populacao' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'ibge_populacao' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'ibge_populacao' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'ibge_populacao' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'regiao_id', + table: 'ibge_populacao' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'ibge_populacao' + } +}).addValue({ + name: 'min_year', + table: 'ibge_populacao', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: 'ibge_populacao', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'city_size', + table: 'ibge_populacao', + tableField: 'porte', + resultField: 'city_size_id', + where: { + relation: '=', + type: 'integer', + field: 'porte' + } +}); + +populationApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + req.sql.from('ibge_populacao') + .field('SUM(ibge_populacao.populacao)', 'total') + .field('ibge_populacao.ano_censo', 'year') + .group('ibge_populacao.ano_censo') + .order('ibge_populacao.ano_censo') + + next(); +}, query, addMissing(rqf), id2str.transform(false), response('population')); + +module.exports = populationApp; diff --git a/src/libs/routes/rateSchool.js b/src/libs/routes/rateSchool.js new file mode 100644 index 0000000000000000000000000000000000000000..856013220997193c288c99ebd83dd33ffe8d03a8 --- /dev/null +++ b/src/libs/routes/rateSchool.js @@ -0,0 +1,323 @@ +const express = require('express'); + +const rateSchoolApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const multiQuery = require(`${libs}/middlewares/multiQuery`); + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const config = require(`${libs}/config`); + +const download = require(`${libs}/middlewares/downloadDatabase`); + +const passport = require('passport'); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +rateSchoolApp.use(cache('15 day')); + +let rqf = new ReqQueryFields(); + +// Complete range of the enrollments dataset. +// Returns a tuple of start and ending years of the complete enrollments dataset. +rateSchoolApp.get('/year_range', (req, res, next) => { + req.sql.from('pnad') + .field('MIN(pnad.ano_censo)', 'start_year') + .field('MAX(pnad.ano_censo)', 'end_year'); + next(); +}, query, response('range')); + +rateSchoolApp.get('/years', (req, res, next) => { + req.sql.from('pnad') + .field('DISTINCT pnad.ano_censo', 'year'); + next(); +}, query, response('years')); + +rateSchoolApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'pnad\''); + next(); +}, query, response('source')); + +rateSchoolApp.get('/ethnic_group_pnad', (req, res, next) => { + req.result = [ + {id: 0, name: 'Indígena'}, + {id: 1, name: 'Branca e amarela'}, + {id: 2, name: 'Preta e parda'}, + {id: 9, name: 'Sem declaração'} + ]; + next(); +}, response('ethnic_group_pnad')); + +rateSchoolApp.get('/age_range', (req, res, next) => { + req.result = [ + {id: 1, name: '0-3'}, + {id: 2, name: '4-5'}, + {id: 3, name: '6-10'}, + {id: 4, name: '11-14'}, + {id: 5, name: '15-17'}, + {id: 6, name: '18-24'} + ]; + next(); +}, response('age_range')); + +rateSchoolApp.get('/gender', (req, res, next) => { + req.result = [ + {id: 1, name: 'Masculino'}, + {id: 2, name: 'Feminino'} + ]; + next(); +}, response('gender')); + +rateSchoolApp.get('/location', (req, res, next) => { + req.result = [ + {id: 1, name: 'Urbana'}, + {id: 2, name: 'Rural'} + ]; + next(); +}, response('location')); + +rateSchoolApp.get('/fifth_household_income', (req, res, next) => { + req.result = [ + {id: 1, name: '20% menores'}, + {id: 2, name: '2o quinto'}, + {id: 3, name: '3o quinto'}, + {id: 4, name: '4o quinto'}, + {id: 5, name: '20% maiores'}, + {id: -1, name: 'Sem declaração'} + ]; + next(); +},response('fifth_household_income')); + +rateSchoolApp.get('/extremes_household_income', (req, res, next) => { + req.result = [ + {id: 1, name: '10% menores'}, + {id: 2, name: '10% maiores'}, + {id: -1, name: 'Sem declaração'} + ]; + next(); +}, response('extremes_household_income')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'pnad' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'pnad' + } +}).addValue({ + name: 'ethnic_group_pnad', + table: 'pnad', + tableField: 'cor_raca', + resultField: 'ethnic_group_pnad_id', + where: { + relation: '=', + type: 'integer', + field: 'cor_raca' + } +}).addValue({ + name: 'min_year', + table: 'pnad', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + table: 'pnad', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: 'pnad', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + table: 'pnad', + field: 'ano_censo' + } +}).addValue({ + name: 'age_range', + table: 'pnad', + tableField: 'faixa_etaria_31_03', + resultField: 'age_range_id', + where: { + relation: '=', + type: 'integer', + field: 'faixa_etaria_31_03' + } +}).addValue({ + name: 'gender', + table: 'pnad', + tableField: 'sexo', + resultField: 'gender_id', + where: { + relation: '=', + type: 'integer', + field: 'sexo' + } +}).addValue({ + name: 'location', + table: 'pnad', + tableField: 'localizacao_id', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'localizacao_id' + } +}).addValue({ + name: 'extremes_household_income', + table: 'pnad', + tableField: 'extremos_nivel_rendimento', + resultField: 'extremes_household_income_id', + where: { + relation: '=', + type: 'integer', + field: 'extremos_nivel_rendimento' + } +}).addValue({ + name: 'fifth_household_income', + table: 'pnad', + tableField: 'quintil_nivel_rendimento', + resultField: 'fifth_household_income_id', + where: { + relation: '=', + type: 'integer', + field: 'quintil_nivel_rendimento' + } +}); + +function matchQueries(queryTotal, queryPartial) { + let match = []; + queryTotal.forEach((result) => { + let newObj = {}; + let keys = Object.keys(result); + keys.forEach((key) => { + newObj[key] = result[key]; + }); + // console.log('NEW OBJ'); + // console.log(newObj); + let index = keys.indexOf('total'); + if(index > -1) keys.splice(index, 1); + let objMatch = null; + + for(let i = 0; i < queryPartial.length; ++i) { + let partial = queryPartial[i]; + let foundMatch = true; + for(let j = 0; j < keys.length; ++j) { + let key = keys[j]; + if(partial[key] !== result[key]) { + foundMatch = false; + break; + } + } + if(foundMatch) { + objMatch = partial; + break; + } + } + + if(objMatch) { + // console.log(objMatch); + newObj.denominator = result.total; + newObj.partial = objMatch.total; + newObj.total = (objMatch.total / result.total) * 100; + match.push(newObj); + } + }); + + return match; +} + +rateSchoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + req.querySet = []; + req.queryIndex = {}; + + log.debug(req.sql.toParam()); + if ("age_range" in req.filter || "age_range" in req.dims) { + let freq_total = req.sql.clone(); + freq_total.field('sum(pnad.peso)', 'total') + .field('pnad.ano_censo','year') + .from('pnad') + .group('pnad.ano_censo') + .order('pnad.ano_censo') + .where('pnad.faixa_etaria_31_03 < 7') + req.queryIndex.freq_total = req.querySet.push(freq_total) - 1; + + let freq_nursery = req.sql.clone(); + freq_nursery.field('sum(pnad.peso)', 'total') + .field('pnad.ano_censo','year') + .from('pnad') + .group('pnad.ano_censo') + .order('pnad.ano_censo') + .where('pnad.frequenta_escola_creche = 2') + .where('pnad.faixa_etaria_31_03 < 7') + req.queryIndex.freq_nursery = req.querySet.push(freq_nursery) - 1; + } + next(); +}, multiQuery, (req, res, next) => { + if ("age_range" in req.filter || "age_range" in req.dims) { + log.debug(req.result[req.queryIndex.freq_total]); + log.debug(req.result[req.queryIndex.freq_nursery]) + let school_attendance_rate = matchQueries(req.result[req.queryIndex.freq_total], req.result[req.queryIndex.freq_nursery]); + req.result = school_attendance_rate; + } else { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } + log.debug(req.result) + next(); +}, id2str.transform(false), response('rateSchool')); + +rateSchoolApp.get('/download', passport.authenticate('bearer', { session: false }), rqf.parse(), rqf.build(), download('pnad', 'mapping_pnad')); + +module.exports = rateSchoolApp; diff --git a/src/libs/routes/region.js b/src/libs/routes/region.js index 773ad347959049dd2137d96abb255a3bc809a2d4..b1076e6484836b8a4911b8d0ada3631f1304e3c0 100644 --- a/src/libs/routes/region.js +++ b/src/libs/routes/region.js @@ -4,23 +4,59 @@ const regionApp = express.Router(); const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); + const squel = require('squel'); -const query = require(`${libs}/middlewares/query`); +const query = require(`${libs}/middlewares/query`).query; const response = require(`${libs}/middlewares/response`); -// Get all regions -regionApp.get('/', (req, res, next) => { - req.sql.from('regioes'); - next(); -}, query, response('region')); - -// Get a region by it's id -regionApp.get('/:id', (req, res, next) => { - req.sql.from('regioes') - .where('pk_regiao_id = ?', parseInt(req.params.id, 10)); +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +regionApp.use(cache('15 day')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addValue({ + name: 'id', + table: '@', + tableField: 'id', + where: { + relation: '=', + type: 'integer', + field: 'id', + table: '@' + } +}).addField({ + name: 'search', + field: false, + where: true +}).addValueToField({ + name: 'name', + table: '@', + tableField: 'nome', + where: { + relation: 'LIKE', + type: 'string', + field: 'nome', + table: '@' + } +}, 'search'); + +regionApp.get('/', rqf.parse(), (req, res, next) => { + req.sql.from('regiao') + .field('id') + .field('nome', 'name'); next(); -}, query, response('region')); +}, rqf.build(), query, response('region')); module.exports = regionApp; diff --git a/src/libs/routes/resetToken.js b/src/libs/routes/resetToken.js new file mode 100644 index 0000000000000000000000000000000000000000..34ece8455adf7b77208dc200a95641ed04638609 --- /dev/null +++ b/src/libs/routes/resetToken.js @@ -0,0 +1,81 @@ +const express = require('express'); + +const resetTokenApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const ResetToken = require(`${libs}/models/resetToken`); + +const User = require(`${libs}/models/user`); + +resetTokenApp.get('/:token', (req, res, next) => { + let token = req.params.token; + ResetToken.findOne({token: token}, (err, rToken) => { + if(err) { + log.error(err); + return next(err); + } + if(!rToken) { + // TODO: generate new reset token + res.statusCode = 404; + return next({msg: 'Token not found', status:404}); + } + if (rToken.hasExpired()) { + res.statusCode = 410; + ResetToken.remove({token: token}, (err) => { + if(err) { + log.error(err); + next(err); + } + }) + return next({msg: 'Token expired', status: 410}); + } + User.findById(rToken.userId, (err, user) => { + if(err) { + log.error(err); + next(err); + } + let u = user.toObject(); + delete u.salt; + delete u.hashedPassword; + res.json({user: u}); + }); + }); +}); +resetTokenApp.post('/:token', (req, res, next) => { + let token = req.params.token; + ResetToken.findOne({token: token}, (err, rToken) => { + if(err) { + log.error(err); + return next(err); + } + if(!rToken) { + res.statusCode = 404; + return next({msg: 'Token not found', status:404}); + } + User.findById(rToken.userId, (err, user) => { + if(err) { + log.error(err); + next(err); + } + user.password = req.body.password; + user.save((err) => { + if(err) { + log.error(err); + next(err); + } + ResetToken.remove({token: token}, (err) => { + if(err) { + log.error(err); + next(err); + } + }) + res.json({msg: "Senha alterada com sucesso"}); + }) + }); + }); +}) + +module.exports = resetTokenApp; diff --git a/src/libs/routes/school.js b/src/libs/routes/school.js index 6c92430b425d3b9fde8c682216c8f6a10f07b75f..0b3742fe9edb9ba0f701443235e0d1bb5592d8a5 100644 --- a/src/libs/routes/school.js +++ b/src/libs/routes/school.js @@ -4,57 +4,486 @@ const schoolApp = express.Router(); const libs = `${process.cwd()}/libs`; +const log = require(`${libs}/log`)(module); + const squel = require('squel'); -const query = require(`${libs}/middlewares/query`); +const query = require(`${libs}/middlewares/query`).query; const response = require(`${libs}/middlewares/response`); -/** - * YOU SHALL NOT PASS - * Esta rota foi desabilitada pois é mais violenta que clube da luta batendo em laranja mecânica - * A api fica sobrecarregada - * Pense na cena do elevador de driver mas o elevador é uma bomba de fusão e demora mais que uma luta do DBz - */ -// schoolApp.get('/', (req, res, next) => { -// req.sql = squel.select().from('escolas') -// .field('pk_escola_id') -// .field('nome_entidade', 'name') -// .field('ano_censo', 'year') -// .field('fk_cod_estado') -// .field('fk_cod_municipio'); -// next(); -// }, query, response('school')); - -// Get a school by it's id -schoolApp.get('/:id', (req, res, next) => { - req.sql.from('escolas') - .where('pk_escola_id = ?', parseInt(req.params.id, 10)); +const id2str = require(`${libs}/middlewares/id2str`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const request = require(`request`); + +const config = require(`${libs}/config`); + +const passport = require('passport'); + +const download = require(`${libs}/middlewares/downloadDatabase`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); +let rqfCount = new ReqQueryFields(); + +// Return location +schoolApp.get('/year_range', cache('15 day'), (req, res, next) => { + req.sql.from('escola') + .field('MIN(escola.ano_censo)', 'start_year') + .field('MAX(escola.ano_censo)', 'end_year') + .where('escola.ano_censo > 2014'); next(); -}, query, response('school')); +}, query, response('range')); -// Get all schools from a state -schoolApp.get('/state/:id', (req, res, next) => { - req.sql.from('escolas') - .field('pk_escola_id') - .field('nome_entidade') - .field('ano_censo') - .field('fk_cod_estado') - .field('fk_cod_municipio') - .where('fk_cod_estado = ?', parseInt(req.params.id, 10)); +schoolApp.get('/years', cache('15 day'), (req, res, next) => { + req.sql.from('escola'). + field('DISTINCT escola.ano_censo', 'year') + .where('escola.ano_censo > 2014'); next(); -}, query, response('school')); +}, query, response('years')); + +schoolApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'escola\''); + next(); +}, query, response('source')); + +schoolApp.get('/location', cache('15 day'), (req, res, next) => { + req.result = [ + {id: 1, name: 'Urbana'}, + {id: 2, name: 'Rural'} + ]; + next(); +}, response('location')); + +schoolApp.get('/adm_dependency', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 4; ++i) { + req.result.push({ + id: i, + name: id2str.admDependency(i) + }); + }; + next(); +}, response('adm_dependency')); + +schoolApp.get('/adm_dependency_detailed', cache('15 day'), (req, res, next) => { + req.result = []; + for(let i = 1; i <= 6; ++i) { + req.result.push({ + id: i, + name: id2str.admDependencyPriv(i) + }); + }; + next(); +}, response('adm_dependency_detailed')); + +schoolApp.get('/government_agreement', cache('15 day'), (req, res, next) => { + req.result = [ + {id: null, name: 'Não Declarado'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('government_agreement')); -// Get all schools from a city -schoolApp.get('/city/:id', (req, res, next) => { - req.sql.from('escolas') - .field('pk_escola_id') - .field('nome_entidade') - .field('ano_censo') - .field('fk_cod_estado') - .field('fk_cod_municipio') - .where('fk_cod_municipio = ?', parseInt(req.params.id, 10)); +schoolApp.get('/agreement', cache('15 day'), (req, res, next) => { + req.result = [ + {id: 1, name: 'Municipal'}, + {id: 2, name: 'Estadual'}, + {id: 3, name: 'Estadual e Municipal'} + ]; + next(); +}, response('agreement')); + +schoolApp.get('/education_day_care_child', (req, res, next) => { + req.result = [ + {id: null, name: 'Não Declarado'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('education_day_care_child')); + +schoolApp.get('/education_preschool_child', cache('15 day'), (req, res, next) => { + req.result = [ + {id: null, name: 'Não Declarado'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('education_preschool_child')); + +schoolApp.get('/education_begin_elementary_school', cache('15 day'), (req, res, next) => { + req.result = [ + {id: null, name: 'Não Declarado'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('education_begin_elementary_school')); + +schoolApp.get('/education_end_elementary_school', cache('15 day'), (req, res, next) => { + req.result = [ + {id: null, name: 'Não Declarado'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('education_end_elementary_school')); + +schoolApp.get('/education_middle_school', cache('15 day'), (req, res, next) => { + req.result = [ + {id: null, name: 'Não Declarado'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('education_middle_school')); + +schoolApp.get('/education_professional', cache('15 day'), (req, res, next) => { + req.result = [ + {id: null, name: 'Não Declarado'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('education_professional')); + +schoolApp.get('/education_eja', cache('15 day'), (req, res, next) => { + req.result = [ + {id: null, name: 'Não Declarado'}, + {id: 0, name: 'Não'}, + {id: 1, name: 'Sim'} + ]; + next(); +}, response('education_eja')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addValue({ + name: 'id', + table: 'escola', + tableField: 'id', + where: { + relation: '=', + type: 'integer', + field: 'id' + } +}).addValue({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'escola' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'escola' + } +}).addValue({ + name: 'year', + table: 'escola', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '=', + type: 'integer', + field: 'ano_censo', + table: 'escola' + } +}); + +rqfCount.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'id', + table: 'escola', + tableField: 'id', + where: { + relation: '=', + type: 'integer', + field: 'id' + } +}).addValueToField({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'escola' + } +}, 'dims').addValueToField({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'escola' + } +}, 'filter').addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: 'escola' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'escola' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'escola' + } +}).addValue({ + name: 'year', + table: 'escola', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '=', + type: 'integer', + field: 'ano_censo', + table: 'escola' + } +}).addValue({ + name: 'location', + table: 'escola', + tableField: 'cod_localizacao', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'cod_localizacao' + } +}).addValue({ + name: 'adm_dependency', + table: 'escola', + tableField: 'dependencia_adm_id', + resultField: 'adm_dependency_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_id' + } +}).addValue({ + name: 'adm_dependency_detailed', + table: 'escola', + tableField: 'dependencia_adm_priv', + resultField: 'adm_dependency_detailed_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_priv' + } +}).addValue({ + name: 'government_agreement', + table: 'escola', + tableField: 'conveniada_pp', + resultField: 'government_agreement_id', + where: { + relation: '=', + type: 'boolean', + field: 'conveniada_pp' + } +}).addValue({ + name: 'agreement', + table: 'escola', + tableField: 'tipo_convenio_pp', + resultField: 'agreement_id', + where: { + relation: '=', + type: 'integer', + field: 'tipo_convenio_pp' + } +}).addValue({ + name: 'education_day_care_child', + table: 'escola', + tableField: 'reg_infantil_creche', + resultField: 'education_day_care_child_id', + where: { + relation: '=', + type: 'boolean', + field: 'reg_infantil_creche' + } +}).addValue({ + name: 'education_preschool_child', + table: 'escola', + tableField: 'reg_infantil_preescola', + resultField: 'education_preschool_child_id', + where: { + relation: '=', + type: 'boolean', + field: 'reg_infantil_preescola' + } +}).addValue({ + name: 'education_begin_elementary_school', + table: 'escola', + tableField: 'reg_fund_ai', + resultField: 'education_begin_elementary_school_id', + where: { + relation: '=', + type: 'boolean', + field: 'reg_fund_ai' + } +}).addValue({ + name: 'education_end_elementary_school', + table: 'escola', + tableField: 'reg_fund_af', + resultField: 'education_end_elementary_school_id', + where: { + relation: '=', + type: 'boolean', + field: 'reg_fund_af' + } +}).addValue({ + name: 'education_middle_school', + table: 'escola', + tableField: 'reg_medio_medio', + resultField: 'education_middle_school_id', + where: { + relation: '=', + type: 'boolean', + field: 'reg_medio_medio' + } +}).addValue({ + name: 'education_professional', + table: 'escola', + tableField: 'educacao_profissional', + resultField: 'education_professional_id', + where: { + relation: '=', + type: 'boolean', + field: 'educacao_profissional' + } +}).addValue({ + name: 'education_eja', + table: 'escola', + tableField: 'ensino_eja', + resultField: 'education_eja_id', + where: { + relation: '=', + type: 'boolean', + field: 'ensino_eja' + } +}).addValue({ + name: 'min_year', + table: 'escola', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: 'escola', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + field: 'ano_censo' + } +}); +schoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + if(typeof req.filter === 'undefined' || Object.keys(req.filter).length === 0) { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } + req.sql.from('escola') + .field('escola.id') + .field('escola.ano_censo', 'year') + .field('escola.nome_escola', 'name') + .field('escola.estado_id', 'state_id') + .field('escola.municipio_id', 'city_id'); next(); }, query, response('school')); +schoolApp.get('/count', cache('15 day'), rqfCount.parse(), rqfCount.build(), (req, res, next) => { + + req.sql.from('escola') + .field('COUNT(escola.id)', 'total') + .field("'Brasil'", 'name') + .field('escola.ano_censo', 'year') + .group('escola.ano_censo') + .order('escola.ano_censo') + .where('escola.situacao_de_funcionamento = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)'); + next(); +}, query, addMissing(rqfCount), id2str.transform(), response('school')); + +schoolApp.get('/count/download', passport.authenticate('bearer', { session: false }), rqfCount.parse(), rqfCount.build(), download('escola', 'mapping_escola')); + module.exports = schoolApp; diff --git a/src/libs/routes/simulation.js b/src/libs/routes/simulation.js new file mode 100644 index 0000000000000000000000000000000000000000..4b2e40c33dd1855116353f618a7c87c13078eb84 --- /dev/null +++ b/src/libs/routes/simulation.js @@ -0,0 +1,167 @@ +const express = require('express'); + +const simulationApp = express(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const Simulation = require(`${libs}/models/simulation`); + +const PQR = require(`${libs}/models/pqr`); + +const passport = require('passport'); + +simulationApp.get('/time', (req, res, next) => { + const maxTime = parseInt(req.query.max_time, 10); + if(isNaN(maxTime)) { + res.status(400); + next({ + status: 400, + message: 'Invalid value for mandatory parameter max_time' + }); + } + res.json({ + result: Array.apply(null, {length: maxTime}).map(Number.call, Number).map((i)=>i+1) + }); +}); + +simulationApp.get('/pqr', (req, res) => { + PQR.findOne((err, pqr) => { + if(err) { + log.error(err); + return next({err}); + } + + res.json(pqr); + }); +}); + +simulationApp.put('/pqr', passport.authenticate('bearer', { session: false }), (req, res, next) => { + let user = req.user.toObject(); + + PQR.findOne((err, pqr) => { + if(err) { + log.error(err) + return next({err}); + } + + if(!user.admin) { + log.info(`Usuário ${user.email} tentou alterar o PQR, mas não tem privilégio`); + res.statusCode = 401; + return next({err: { msg: 'Unauthorized'}}); + } + pqr.content = req.body.content || pqr.content; + pqr.save((err) => { + if(err) { + log.error(err); + return next({err}); + } + res.json({msg: 'PQR updated'}) + }); + }); +}); + +simulationApp.get('/', passport.authenticate('bearer', { session: false }), (req, res) => { + let user = req.user.toObject(); + let query = Simulation.find({userId: user._id}).select('userId name createdAt updatedAt'); + query.exec((err, simulations) => { + if(err) { + log.error(err); + return next({err}); + } + + res.json(simulations); + }); + + // Simulation.find({userId: user._id}, (err, simulations) => { + // if(err) { + // log.error(err); + // return next({err}); + // } + + // res.json(simulations); + // }); +}); + +simulationApp.post('/', passport.authenticate('bearer', { session: false }), (req, res, next) => { + let user = req.user.toObject(); + + let simulation = new Simulation({ + userId: user._id, + content: req.body.content, + name: req.body.name + }); + + simulation.save((err) => { + if(err) { + log.error(err); + return next({err}); + } + + res.json({msg: 'Simulation created', simulation}); + }) +}); + +simulationApp.get('/:id', passport.authenticate('bearer', { session: false }), (req, res) => { + let user = req.user.toObject(); + + Simulation.findOne({_id: req.params.id, userId: user._id}, (err, simulation) => { + if(err) { + log.error(err); + return next({err}); + } + + res.json(simulation); + }); +}); + +simulationApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => { + let user = req.user.toObject(); + + Simulation.findOne({_id: req.params.id, userId: user._id}, (err, simulation) => { + if(err) { + log.error(err); + return next({err}); + } + + if(!simulation) { + res.statusCode = 404; + return next({err: { msg: 'Simulation not found'}}); + } + + simulation.content = req.body.content || simulation.content; + simulation.name = req.body.name || simulation.name; + simulation.updatedAt = Date.now(); + + simulation.save((err) => { + if(err) { + log.error(err); + return next(err); + } + + res.json(simulation); + }); + }); +}); + +simulationApp.delete('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => { + let user = req.user.toObject(); + + Simulation.remove({_id: req.params.id, userId: user._id}, (err, simulation) => { + if(err) { + log.error(err); + return next({err}); + } + + res.json({msg: 'Simulation removed'}); + }); +}); + +module.exports = simulationApp; diff --git a/src/libs/routes/siope.js b/src/libs/routes/siope.js new file mode 100644 index 0000000000000000000000000000000000000000..51c1c36833b99ff097cf8a85a0c90c20598f737b --- /dev/null +++ b/src/libs/routes/siope.js @@ -0,0 +1,166 @@ +const express = require('express'); + +const siopeApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const multiQuery = require(`${libs}/middlewares/multiQuery`); + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +siopeApp.use(cache('15 day')); + +siopeApp.get('/years', (req, res, next) => { + req.sql.from('siope_mun') + .field('DISTINCT siope_mun.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + req.oldResult = req.result; + + req.sql = squel.select(); + + req.sql.from('siope_uf') + .field('DISTINCT siope_uf.ano_censo', 'year'); + next(); +}, query, (req, res, next) => { + let result = Object.assign(req.oldResult, req.result); + req.result = result; + next(); +}, response('years')); + +rqf.addField({ + name: 'filter', + field: true, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'municipio_id', + table: 'siope_mun' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'siope_mun' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: ['id','nome','sigla'], + resultField: ['state_id','state_name','state_abbreviation'], + where: { + relation: '=', + type: 'integer', + field: 'estado_id', + table: '@' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: '@' + } +}).addValue({ + name: 'min_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: '@', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + table: '@', + field: 'ano_censo' + } +}); + + + +siopeApp.get('/', rqf.parse(), (req, res, next) => { + req.querySet = []; + req.queryIndex = {}; + + let siopeUf = req.sql.clone(); + siopeUf.from('siope_uf') + .field('siope_uf.ano_censo', 'year') + .field('siope_uf.estado_id', 'state_id') + .field('siope_uf.fundeb', 'fundeb') + .field('siope_uf.total_impostos', 'impostos') + .field('siope_uf.total_mde', 'MDE') + .group('siope_uf.ano_censo') + .group('siope_uf.estado_id') + .group('siope_uf.fundeb') + .group('siope_uf.total_impostos') + .group('siope_uf.total_mde') + .order('siope_uf.ano_censo'); + req.queryIndex.siopeUf = req.querySet.push(siopeUf) - 1; + + let siopeMun = req.sql.clone(); + siopeMun.from('siope_mun') + .field('siope_mun.ano_censo', 'year') + .field('siope_mun.estado_id', 'state_id') + .field('siope_mun.municipio_id', 'city_id') + .field('siope_mun.fundeb', 'fundeb') + .field('siope_mun.total_impostos', 'impostos') + .field('siope_mun.total_mde', 'MDE') + .group('siope_mun.ano_censo') + .group('siope_mun.estado_id') + .group('siope_mun.municipio_id') + .group('siope_mun.fundeb') + .group('siope_mun.total_impostos') + .group('siope_mun.total_mde') + .order('siope_mun.ano_censo'); + req.queryIndex.siopeMun = req.querySet.push(siopeMun) - 1; + + next(); +}, rqf.multibuild(), multiQuery, (req, res, next) => { + + let result = [] + + req.result[req.queryIndex.siopeUf].forEach((item) => { + result.push(item) + }); + req.result[req.queryIndex.siopeMun].forEach((item) => { + result.push(item) + }); + + req.result = result; + next(); + +}, response('siope')); + +module.exports = siopeApp; diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js new file mode 100644 index 0000000000000000000000000000000000000000..5523de7ad8fa79fa357f16dce588cc53782bc2ff --- /dev/null +++ b/src/libs/routes/spatial.js @@ -0,0 +1,353 @@ +const express = require('express'); + +const libs = `${process.cwd()}/libs`; + +const squel = require('squel'); + +const log = require(`${libs}/log`)(module); + +const query = require(`${libs}/middlewares/query`).query; + +const sqlQuery = require(`${libs}/db/query_exec`); + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const spatialApp = express(); + +let rqf = new ReqQueryFields(); + +function processResultSet(querySet, querySetLabels = ["result"], singleResult = false) { + const resultMap = new Map(); + let resultIdx = 0; + // loop relies on the fact that Promise.all maintains the order of the original iterable + for(let result of querySet) { + const resultLbl = querySetLabels[resultIdx]; + resultMap[resultLbl] = []; + if (singleResult) { + resultMap[resultLbl] = result[0]; + } else { + for(let row of result) { + resultMap[resultLbl].push(row); + } + } + resultIdx++; + } + return resultMap; +} + +function dbExecAll(querySet = []) { + // Issue all queries concurrently to the database, for every query object in the iterable + // NOTE: Array.map() returns a copy of the original array with each object 'mapped'. + return querySet.map((qry) => { return sqlQuery(qry.toString()); }); +} + +rqf.addField({ + name: 'filter', + field: true, + where: true +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'regiao_id' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'estado_id' + } +}).addValue({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'municipio_id' + } +}).addValue({ + name: 'school', + table: 'escola', + tableField: 'nome_escola', + resultField: 'school_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + } +}); + +spatialApp.get('/sociodemographic', rqf.parse(), rqf.build(), (req, res, next) => { + const populationYearQry = squel.select() + .field('MAX(ibge_populacao.ano_censo)') + .from('ibge_populacao'); + + const populationQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('SUM(populacao)', 'population') + .field('ibge_populacao.ano_censo', 'census_year') + .from('ibge_populacao') + .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`) + .group('ibge_populacao.ano_censo'); + + const pibYearQry = squel.select() + .field('MAX(ibge_pib.ano_censo)') + .from('ibge_pib'); + + const pibQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita') + .field('ibge_pib.ano_censo', 'census_year') + .from('ibge_pib') + .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`) + .group('ibge_pib.ano_censo'); + + const idhYearQry = squel.select() + .field('MAX(adh_idh.ano_censo)') + .from('adh_idh'); + + const idhQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('AVG(idhm)', 'idhm') + .field('adh_idh.ano_censo', 'census_year') + .from('adh_idh') + .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`) + .group('adh_idh.ano_censo'); + + const analfabYearQry = squel.select() + .field('MAX(adh_analfabetismo.ano_censo)') + .from('adh_analfabetismo'); + + const analfabQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('AVG(t_analf15m)', 'analfabetism') + .field('adh_analfabetismo.ano_censo', 'census_year') + .from('adh_analfabetismo') + .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`) + .group('adh_analfabetismo.ano_censo'); + + const giniYearQry = squel.select() + .field('MAX(adh_gini.ano_censo)') + .from('adh_gini'); + + const giniQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('AVG(gini)', 'gini') + .field('adh_gini.ano_censo', 'census_year') + .from('adh_gini') + .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`) + .group('adh_gini.ano_censo'); + + // map query objects to their respective response labels + const queryLabels = [ "population", "gdp", "idh", "analfab", "gini" ]; + const querySet = [ populationQry, pibQry, idhQry, analfabQry, giniQry ]; + // wait until all queries finish or one of them fail + Promise.all(dbExecAll(querySet)).then((queryResults) => { + req.result = processResultSet(queryResults, queryLabels, true); + next(); + }).catch((error) => { + log.error(`[SQL query error] ${error}`); + next(error); + }); +}, response('spatial')); + +spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => { + const censusYearQry = squel.select() + .field('MAX(escola.ano_censo)', 'ano_censo') + .from('escola') + .toString(); + + const totalSchoolsQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('\'Total\'', 'location_name') + .field('COUNT(DISTINCT(escola.id))', 'total') + .field('escola.ano_censo', 'census_year') + .from('turma') + .from('escola') + .where('escola.ano_censo=turma.ano_censo AND escola.id=turma.escola_id') + .where(`escola.ano_censo IN (${censusYearQry})`) + .where('turma.tipo_turma_id = 0') + .group('escola.ano_censo'); + + const schoolsPerLocationQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COUNT(DISTINCT(escola.id))', 'total') + .field('escola.ano_censo', 'census_year') + .field('localizacao.descricao', 'location_name') + .from('localizacao') + .from('turma') + .from('escola') + .where('escola.cod_localizacao=localizacao.id') + .where('escola.ano_censo=turma.ano_censo AND escola.id=turma.escola_id') + .where(`escola.ano_censo IN (${censusYearQry})`) + .where('turma.tipo_turma_id = 0') + .group('escola.cod_localizacao') + .group('escola.ano_censo') + .group('localizacao.descricao') + .order('localizacao.descricao'); + + const schoolsPerAdmDependencyQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COUNT(DISTINCT(escola.id))', 'total') + .field('escola.ano_censo', 'census_year') + .field('dependencia_adm.nome', 'adm_dependency_name') + .from('dependencia_adm') + .from('escola') + .where('escola.dependencia_adm_id=dependencia_adm.id') + .where(`escola.ano_censo IN (${censusYearQry})`) + .group('escola.ano_censo') + .group('dependencia_adm.nome') + .order('escola.ano_censo') + .order('dependencia_adm.nome'); + + const enrollmentsQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(SUM(uc201.matriculas), 0)', 'total') + .field('uc201.ano_censo', 'census_year') + .from('uc201') + .group('uc201.ano_censo'); + + const enrollmentsPerAdmDepQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(SUM(uc201.matriculas), 0)', 'total') + .field('uc201.ano_censo', 'census_year') + .field('dependencia_adm.nome', 'adm_dependency_name') + .from('dependencia_adm') + .from('uc201') + .where('uc201.dependencia_adm_id=dependencia_adm.id') + .group('dependencia_adm.nome') + .group('uc201.ano_censo'); + + const enrollmentsPerSchoolLevelQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(SUM(uc201.matriculas), 0)', 'total') + .field('uc201.ano_censo', 'census_year') + .field('etapa_ensino.desc_etapa', 'school_level_name') + .field('dependencia_adm.nome', 'adm_dependency_name') + .from('etapa_ensino') + .from('dependencia_adm') + .from('uc201') + .where('uc201.etapas_mod_ensino_segmento_id=etapa_ensino.id') + .where('uc201.dependencia_adm_id=dependencia_adm.id') + .group('etapa_ensino.desc_etapa') + .group('dependencia_adm.nome') + .group('uc201.ano_censo'); + + const enrollmentsPerLocationQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(SUM(uc201.matriculas), 0)', 'total') + .field('uc201.ano_censo', 'census_year') + .field('localizacao.descricao', 'location_name') + .from('localizacao') + .from('uc201') + .where('uc201.localizacao=localizacao.id') + .group('localizacao.descricao') + .group('uc201.ano_censo'); + + const queryLabels = [ "school", "school_per_location", "school_per_adm_dependency", "enrollment", "enrollment_per_adm_dep", + "enrollment_per_school_level", "enrollment_per_location" ]; + const querySet = [ totalSchoolsQry, schoolsPerLocationQry, schoolsPerAdmDependencyQry, enrollmentsQry, + enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry, enrollmentsPerLocationQry]; + // wait until all queries finish or one of them fail + Promise.all(dbExecAll(querySet)).then((queryResults) => { + req.result = processResultSet(queryResults, queryLabels); + next(); + }).catch((error) => { + log.error(`[SQL query error] ${error}`); + next(error); + }); +}, response('spatial')); + +spatialApp.get('/educational/school_level', rqf.parse(), rqf.build(), (req, res, next) => { + const enrollmentsPerSchoolLevelYearQry = squel.select() + .field('MAX(matricula.ano_censo)', 'census_year') + .from('matricula'); + + const enrollmentsPerSchoolLevelQry = req.sql.clone() + .field('COALESCE(COUNT(matricula.id), 0)', 'total') + .field('matricula.ano_censo', 'census_year') + .field('matricula.serie_ano_id', 'school_year') + .field('etapa_ensino.desc_etapa', 'school_level') + .from('etapa_ensino') + .from('matricula') + .where(`matricula.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`) + .where('matricula.etapa_ensino_id = etapa_ensino.id') + .where('matricula.tipo <= 3') + .group('etapa_ensino.desc_etapa') + .group('etapa_ensino.id') + .group('matricula.serie_ano_id') + .group('matricula.ano_censo') + .order('etapa_ensino.id') + .order('matricula.serie_ano_id') + .order('matricula.ano_censo'); + + const queryLabels = [ 'enrollment_per_school_level', 'enrollment_census_year' ]; + const querySet = [ enrollmentsPerSchoolLevelQry, enrollmentsPerSchoolLevelYearQry ]; + // wait until all queries finish or one of them fail + Promise.all(dbExecAll(querySet, enrollmentsPerSchoolLevelYearQry)).then((queryResults) => { + const result = queryResults[0]; + const censusYear = queryResults[1][0]['census_year']; + + let school_levels = {}; + for(let i = 0; i < result.length; ++i) { + const school_year = id2str.schoolYear(result[i].school_year); + const school_level = result[i].school_level; + const census_year = result[i].census_year; + if (typeof school_levels[school_level] === 'undefined') { + school_levels[school_level] = {}; + } + school_levels[school_level][school_year] = parseInt(result[i].total, 10); + } + + let response = []; + for(let level in school_levels) { + if (school_levels.hasOwnProperty(level)) { + let sclevel = {}; + sclevel["degree"] = level; + sclevel["census_year"] = parseInt(censusYear, 10); + sclevel["table"] = []; + for(let school_year in school_levels[level]) { + if (school_levels[level].hasOwnProperty(school_year)) { + let enrollment = { 'title' : school_year, + 'value' : school_levels[level][school_year] }; + sclevel["table"].push(enrollment); + } + } + response.push(sclevel); + } + } + req.result = response; + next(); + }).catch((error) => { + log.error(`[SQL query error] ${error}`); + next(error); + }); +}, response('spatial')); + +module.exports = spatialApp; diff --git a/src/libs/routes/state.js b/src/libs/routes/state.js index 75e1c0b7835901804ea78e459b1fe0050ec03631..33778869342216fe40e8e5104f327d4f2de804ef 100644 --- a/src/libs/routes/state.js +++ b/src/libs/routes/state.js @@ -6,27 +6,74 @@ const libs = `${process.cwd()}/libs`; const squel = require('squel'); -const query = require(`${libs}/middlewares/query`); +const query = require(`${libs}/middlewares/query`).query; const response = require(`${libs}/middlewares/response`); -// Get all states -stateApp.get('/', (req, res, next) => { - req.sql.from('estados'); - next(); -}, query, response('state')); +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); -// Get a state -stateApp.get('/:id', (req, res, next) => { - req.sql.from('estados') - .where('pk_estado_id = ?', parseInt(req.params.id, 10)); - next(); -}, query, response('state')); +const config = require(`${libs}/config`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +stateApp.use(cache('15 day')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addValue({ + name: 'id', + table: 'estado', + tableField: 'id', + where: { + relation: '=', + type: 'integer', + field: 'id' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'regiao_id', + table: 'estado' + }, + join: { + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'estado' + } +}).addField({ + name: 'search', + field: false, + where: true +}).addValueToField({ + name: 'name', + table: 'estado', + tableField: 'nome', + where: { + relation: 'LIKE', + type: 'string', + field: 'nome' + } +}, 'search'); -// Get all states from a region -stateApp.get('/region/:id', (req, res, next) => { - req.sql.from('estados') - .where('fk_regiao_id = ?', parseInt(req.params.id, 10)); +stateApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + req.sql.from('estado') + .field('estado.id') + .group('estado.id') + .field('regiao_id', 'region_id') + .group('regiao_id') + .field('estado.nome', 'name') + .group('estado.nome') + .field('estado.sigla', 'abbreviation') + .group('estado.sigla'); next(); }, query, response('state')); diff --git a/src/libs/routes/teacher.js b/src/libs/routes/teacher.js new file mode 100644 index 0000000000000000000000000000000000000000..a23293de7d7d9343ea435daca95c8016ecb64156 --- /dev/null +++ b/src/libs/routes/teacher.js @@ -0,0 +1,429 @@ +const express = require('express'); + +const teacherApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const squel = require('squel'); + +const query = require(`${libs}/middlewares/query`).query; + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const id2str = require(`${libs}/middlewares/id2str`); + +const config = require(`${libs}/config`); + +const passport = require('passport'); + +const download = require(`${libs}/middlewares/downloadDatabase`); + +const addMissing = require(`${libs}/middlewares/addMissing`); + +const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware; + +let rqf = new ReqQueryFields(); + +teacherApp.use(cache('15 day')); + +// Returns a tuple of start and ending years of the complete enrollments dataset. +teacherApp.get('/year_range', (req, res, next) => { + req.sql.from('docente') + .field('MIN(docente.ano_censo)', 'start_year') + .field('MAX(docente.ano_censo)', 'end_year'); + next(); +}, query, response('range')); + +teacherApp.get('/years', (req, res, next) => { + req.sql.from('docente'). + field('DISTINCT docente.ano_censo', 'year'); + next(); +}, query, response('years')); + +teacherApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'docente\''); + next(); +}, query, response('source')); + +teacherApp.get('/adm_dependency_detailed', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 6; ++i) { + req.result.push({ + id: i, + name: id2str.admDependencyPriv(i) + }); + }; + next(); +}, response('adm_dependency_detailed')); + +teacherApp.get('/adm_dependency', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 4; ++i) { + req.result.push({ + id: i, + name: id2str.admDependency(i) + }); + }; + next(); +}, response('adm_dependency')); + +teacherApp.get('/education_level_mod', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 11; ++i) { + req.result.push({ + id: i, + name: id2str.educationLevelMod(i) + }); + } + + req.result.push({ + id: 99, + name: id2str.educationLevelMod(99) + }); + next(); +}, response('education_level_mod')); + +teacherApp.get('/education_level_short', (req, res, next) => { + req.result = [ + {id: null, name: 'Não classificada'}, + {id: 1, name: 'Creche'}, + {id: 2, name: 'Pré-Escola'}, + {id: 3, name: 'Ensino Fundamental - anos iniciais'}, + {id: 4, name: 'Ensino Fundamental - anos finais'}, + {id: 5, name: 'Ensino Médio'}, + {id: 6, name: 'EJA'}, + {id: 7, name: 'EE exclusiva'} + ]; + next(); +}, response('education_level_short')); + +teacherApp.get('/location', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 2; ++i) { + req.result.push({ + id: i, + name: id2str.location(i) + }); + }; + next(); +}, response('location')); + +teacherApp.get('/rural_location', (req, res, next) => { + req.result = [ + {id: 1, name: "Urbana"}, + {id: 2, name: "Rural"}, + {id: 3, name: "Rural - Área de assentamento"}, + {id: 4, name: "Rural - Terra indígena"}, + {id: 5, name: "Rural - Área remanescente de quilombos"}, + {id: 6, name: "Rural - Unidade de uso sustentável"} + ]; + next(); +}, response('rural_location')); + +teacherApp.get('/education_type', (req, res, next) => { + req.sql.from('docente') + .field('DISTINCT nivel_tipo_formacao', 'id') + .order('id'); + next(); +}, query, (req, res, next) => { + req.result.forEach((result) => { + result.name = id2str.educationType(result.id); + }); + next(); +}, response('education_type')); + +teacherApp.get('/gender', (req, res, next) => { + req.result = [ + {id: 1, name: 'Masculino'}, + {id: 2, name: 'Feminino'} + ]; + next(); +}, response('gender')); + + +teacherApp.get('/contract_type', (req, res, next) => { + req.result = [ + {id: 1, name: 'Concursado/Efetivo/Estável'}, + {id: 2, name: 'Contrato temporário'}, + {id: 3, name: 'Contrato terceirizado'}, + {id: 4, name: 'Contrato CLT'} + ]; + next(); +}, response('contract_type')); + +teacherApp.get('/ethnic_group', (req, res, next) => { + req.result = []; + for(let i = 0; i <=5; ++i) { + req.result.push({ + id: i, + name: id2str.ethnicGroup(i) + }); + } + next(); +}, response('ethnic_group')); + +rqf.addField({ + name: 'filter', + field: false, + where: true +}).addField({ + name: 'dims', + field: true, + where: false +}).addValue({ + name: 'adm_dependency', + table: 'docente', + tableField: 'dependencia_adm_id', + resultField: 'adm_dependency_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_id' + } +}).addValue({ + name: 'adm_dependency_detailed', + table: 'docente', + tableField: 'dependencia_adm_priv', + resultField: 'adm_dependency_detailed_id', + where: { + relation: '=', + type: 'integer', + field: 'dependencia_adm_priv' + } +}).addValue({ + name: 'contract_type', + table: 'docente', + tableField: 'tipo_contratacao', + resultField: 'contract_type_id', + where: { + relation: '=', + type: 'integer', + field: 'tipo_contratacao' + } +}).addValue({ + name: 'education_level_mod', + table: 'docente', + tableField: 'etapas_mod_ensino_segmento_id', + resultField: 'education_level_mod_id', + where: { + relation: '=', + type: 'integer', + field: 'etapas_mod_ensino_segmento_id' + } +}).addValue({ + name: 'education_level_short', + table: 'docente', + tableField: 'etapa_resumida', + resultField: 'education_level_short_id', + where: { + relation: '=', + type: 'integer', + field: 'etapa_resumida' + } +}).addValue({ + name: 'education_type', + table: 'docente', + tableField: 'nivel_tipo_formacao', + resultField: 'education_type_id', + where: { + relation: '=', + type: 'integer', + field: 'nivel_tipo_formacao' + } +}).addValue({ + name: 'region', + table: 'regiao', + tableField: 'nome', + resultField: 'region_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'escola_regiao_id', + foreignTable: 'docente' + } +}).addValue({ + name: 'state', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'escola_estado_id', + foreignTable: 'docente' + } +}).addValueToField({ + name: 'city', + table: 'municipio', + tableField: ['nome', 'id'], + resultField: ['city_name', 'city_id'], + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'escola_municipio_id', + foreignTable: 'docente' + } +}, 'dims').addValueToField({ + name: 'city', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'escola_municipio_id', + foreignTable: 'docente' + } +}, 'filter').addValue({ + name: 'school', + table: 'escola', + tableField: 'nome_escola', + resultField: 'school_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: ['id', 'ano_censo'], + foreign: ['escola_id', 'ano_censo'], + foreignTable: 'docente' + } +}).addValue({ + name: 'location', + table: 'docente', + tableField: 'cod_localizacao', + resultField: 'location_id', + where: { + relation: '=', + type: 'integer', + field: 'cod_localizacao' + } +}).addValue({ + name: 'rural_location', + table: 'docente', + tableField: 'localidade_area_rural', + resultField: 'rural_location_id', + where: { + relation: '=', + type: 'integer', + field: 'localidade_area_rural' + } +}).addValue({ + name: 'min_year', + table: 'docente', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '>=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'max_year', + table: 'docente', + tableField: 'ano_censo', + resultField: 'year', + where: { + relation: '<=', + type: 'integer', + field: 'ano_censo' + } +}).addValue({ + name: 'gender', + table: 'docente', + tableField: 'sexo', + resultField: 'gender_id', + where: { + relation: '=', + type: 'integer', + field: 'sexo' + } +}).addValue({ + name: 'ethnic_group', + table: 'docente', + tableField: 'cor_raca', + resultField: 'ethnic_group_id', + where: { + relation: '=', + type: 'integer', + field: 'cor_raca' + } +}); + +teacherApp.get('/', rqf.parse(), (req, res, next) => { + req.sql.field('COUNT(DISTINCT docente.id)', 'total') + .field("'Brasil'", 'name') + .field('docente.ano_censo', 'year') + .from('docente') + .join('turma', null, 'docente.turma_id=turma.id AND docente.ano_censo=turma.ano_censo') + .group('docente.ano_censo') + .order('docente.ano_censo') + .where('(docente.tipo_docente = 1 OR docente.tipo_docente = 5) AND (turma.tipo_turma_id <= 3)'); + + if("education_level_mod" in req.dims) { + delete req.dims.education_level_mod; + req.hadEducationLevelMod = true; + req.sql.field('docente.etapas_mod_ensino_segmento_id', 'education_level_mod_id') + .where('docente.etapas_mod_ensino_segmento_id < 11') + .group('docente.etapas_mod_ensino_segmento_id') + .order('docente.etapas_mod_ensino_segmento_id'); + } + + next(); +}, rqf.build(), query, addMissing(rqf), (req, res, next) => { + req.oldResult = req.result; + if(req.hadEducationLevelMod) { + + req.sql = squel.select() + .field('COUNT(DISTINCT docente.id)', 'total') + .field("'Brasil'", 'name') + .field('docente.ano_censo', 'year') + .from('docente') + .join('turma', null, 'docente.turma_id=turma.id AND docente.ano_censo=turma.ano_censo') + .group('docente.ano_censo') + .order('docente.ano_censo') + .where('(docente.tipo_docente = 1 OR docente.tipo_docente = 5) AND (turma.tipo_turma_id <= 3)') + .where('docente.profissionalizante = 1'); + + rqf.build()(req, res, () => {}); + query(req, res, next); + } else { + next(); + } +}, (req, res, next) => { + if(req.hadEducationLevelMod) { + req.result.forEach((result) => { + result.education_level_mod_id = 11; + req.oldResult.push(result); + }); + } + req.result = req.oldResult; + next(); +}, id2str.transform(), response('teacher')); + +teacherApp.get('/download', passport.authenticate('bearer', { session: false }), rqf.parse(), rqf.build(), download('docente', 'mapping_docente')); + +module.exports = teacherApp; diff --git a/src/libs/routes/user.js b/src/libs/routes/user.js new file mode 100644 index 0000000000000000000000000000000000000000..af123a588d41a37c1e64fb0cfa20abe3928a84b4 --- /dev/null +++ b/src/libs/routes/user.js @@ -0,0 +1,308 @@ +const express = require('express'); + +const userApp = express(); + +const libs = `${process.cwd()}/libs`; + +const config = require(`${libs}/config`); + +const log = require(`${libs}/log`)(module); + +const User = require(`${libs}/models/user`); + +const VerificationToken = require(`${libs}/models/verificationToken`); + +const ResetToken = require(`${libs}/models/resetToken`); + +const response = require(`${libs}/middlewares/response`); + +const email = require(`${libs}/middlewares/email`); + +const passport = require('passport'); + +function emailSyntax(email) { + const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i; + return regex.test(email); +} + +userApp.get('/schooling', (req, res, next) => { + req.result = [ + 'Não estudou', + 'Ensino Fundamental Incompleto', + 'Ensino Fundamental Completo', + 'Ensino Médio', + 'Graduação', + 'Mestrado', + 'Doutorado' + ]; + next(); +}, response('schooling')); + +userApp.get('/segment', (req, res, next) => { + req.result = [ + 'Gestores e equipe gestora das secretarias e ministério da Educação', + 'Gestores dos órgãos de planejamento e finanças (das três esferas de governo)', + 'Agentes do poder legislativo', + 'Agentes dos conselhos de educação', + 'Profissionais da educação', + 'Sindicato', + 'Sociedade civil interessada no financiamento da Educação Básica de qualidade', + 'Comunidade acadêmica', + 'Imprensa', + 'Outro [citar segmento]' + ]; + next(); +}, response('segment')); + +userApp.get('/role', (req, res, next) => { + req.result = [ + {"Gestores e equipe gestora das secretarias e ministério da Educação" : ["Dirigente municipal, estadual e federal", "Secretário do MEC", "Servidor da área de planejamento educacional", "Membro de associação de gestores (Ex. Undime, Consed, etc)", "Outro [citar função]"]}, + {"Gestores dos órgãos de planejamento e finanças (das três esferas de governo)" : ["Equipe gestora dos órgãos de planejamento", "Equipe gestora dos órgãos de finanças", "Outro [citar função]"]}, + {"Agentes do poder legislativo" : ["Parlamentar", "Assessor/a parlamentar", "Auditor/a dos tribunais de conta", "Conselheiro/a de tribunais de conta.", "Outro [citar função]"]}, + {"Agentes dos conselhos de educação" : ["Conselheiro/a municipais, estaduais e federais", "Conselheiro/a do Fundeb", "Outro [citar função]"]}, + {"Profissionais da educação" : ["Professor/a da Educação Básica", "Profissional da educação não-docente", "Outro [citar função]"]}, + {"Sindicato" : ["Agente de sindicatos"]}, + {"Sociedade civil interessada no financiamento da Educação Básica de qualidade" : ["Membro de fóruns educacionais", "Membro de ONGs e demais entidades sem fins lucrativos", "Estudante da educação básica e membro de entidades estudantis", "Pais e membros de entidades de pais", "Outro [citar função]"]}, + {"Comunidade acadêmica" : ["Pesquisador/a", "Estudantes de graduação e pós-graduação", "Representantes de entidades de pesquisa (Ex.: ANPED, ANPAE e FINEDUCA)", "Outro [citar função]"]}, + {"Imprensa" : ["Jornalista", "Outro [citar função]"]}, + {"Outro [citar segmento]" : []} + ] + next(); +}, response('role')); + +userApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, next) => { + User.find((err, users) => { + if(err) { + log.error(err); + return next(err); + } + + let result = []; + users.forEach((user) => { + let u = user.toObject(); + delete u.hashedPassword; + delete u.salt; + result.push(u); + }); + req.result = result; + next(); + }); +}, response('users')); + +userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, res, next) => { + let user = req.user.toObject(); + delete user.hashedPassword; + delete user.salt; + req.result = user; + next(); +}, response('user')); + +userApp.get('/:id', (req, res, next) => { + User.findById(req.params.id, (err, user) => { + if(err) { + log.error(err); + return next(err); + } + if(!user) { + req.statusCode = 404; + next({msg: 'User not found'}); + } else { + let u = user.toObject; + delete u.hashedPassword; + delete u.salt; + req.result = u; + next(); + } + }); +}, response('user')); + +userApp.post('/', (req, res, next) => { + let user = new User({ + email: req.body.email, + password: req.body.password, + name: req.body.name, + nickname: req.body.nickname, + cpf: req.body.cpf, + cep: req.body.cep, + complement: req.body.complement, + address: req.body.address, + phone: req.body.phone, + schooling: req.body.schooling, + course: req.body.course, + segment: req.body.segment, + role: req.body.role, + institutionName: req.body.institutionName, + state: req.body.state, + city: req.body.city, + receiveEmails: false || req.body.receiveEmails, + origin: req.body.origin, + citesegment: req.body.citesegment, + citerole: req.body.citerole, + admin: false + }); + + if (typeof req.body.password === 'undefined' || !req.body.password) { + res.statusCode = 400; + return res.json({errors: ["O campo senha é obrigatório"]}); + } else { + user.save((err) => { + if(err) { + log.error(err); + let errors = []; + for(let errName in err.errors) { + errors.push(err.errors[errName].message); + } + log.error(errors); + res.statusCode = 400; + return res.json({err, errors}); + } + + // Create verification token + let verificationToken = new VerificationToken({ + userId: user._id + }); + + verificationToken.createVerificationToken((err, token) => { + if(err) { + log.error(err); + return next(err); + } + let url = config.default.lde.url + '/verify'; + let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${token}`; + // Send confirmation email + let mailOptions = { + to: `"${user.name} <${user.email}>"`, + subject: "Confirme seu cadastro - Laboratório de Dados Educacionais", + text + } + email(mailOptions, (err, info) => { + if(err) { + log.error(err); + res.json({msg: 'User created'}); + } + if(info) { + log.info(`Message ${info.messageId} sent: ${info.response}`); + log.info(`Usuário ${user.email} foi criado`); + } + res.json({msg: 'User created'}); + }); + }); + }); + } + +}); + +userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => { + console.log(req.params.id); + console.log(req.user._id); + User.findById(req.params.id, (err, user) => { + if (err) { + log.error(err); + return next({err}); + } + + if(!user) { + res.statusCode = 404; + return next({err: { + message: 'Usuário não encontrado' + }}); + } + + console.log(req.body); + + user.email = req.body.email || user.email; + user.name = req.body.name || user.name; + user.nickname = req.body.nickname || user.nickname || user.name; + user.cep = req.body.cep || user.cep; + user.complement = req.body.complement || user.complement; + user.address = req.body.address || user.address; + user.phone = req.body.phone || user.phone; + user.schooling = req.body.schooling || user.schooling; + user.course = req.body.course || user.course; + user.segment = req.body.segment || user.segment; + user.role = req.body.role || user.role; + user.institutionName = req.body.institutionName || user.institutionName; + user.state = req.body.state || user.state; + user.city = req.body.city || user.city; + user.receiveEmails = req.body.receiveEmails || user.receiveEmails; + user.citesegment = req.body.citesegment || user.citesegment; + user.citerole = req.body.citerole || user.citerole; + + // console.log(user.checkPassword(req.body.password)); + if ((req.body.password) && (req.body.newpassword)) { + if (req.body.password != req.body.newpassword) { + if (user.checkPassword(req.body.password)) { + user.password = req.body.newpassword; + } else { + res.statusCode = 500; + return res.json({error: { + message: 'A senha atual está incorreta' + }}); + } + } else { + res.statusCode = 500; + return res.json({error: { + message: 'A nova senha é a mesma da senha atual' + }}); + } + } + + user.save(err => { + if(err) { + log.error(err); + return next({message: 'Erro ao atualizar usuário'}); + } + let u = user.toObject(); + delete u.hashedPassword; + delete u.salt; + res.json({user: u}); + }) + }) +}); + +userApp.get('/reset/password', (req, res, next) => { + let emailAddress = req.query.email; + User.findOne({email: emailAddress}, (err, user)=> { + if(err) { + log.error(err); + let errors = []; + for(let errName in err.errors) { + errors.push(err.errors[errName].message); + } + res.statusCode = 400; + return res.json({err, errors}); + } + if (!user) { + res.statusCode = 404; + res.json({msg: "O usuário não está cadastrado"}); + } + else { + let resetToken = new ResetToken({ + userId: user._id + }); + resetToken.createResetToken((err, token) => { + if (err) { + log.error(err); + return next(err); + } + let url = config.default.lde.url + '/reset-password'; + let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${token}`; + let mailOptions = { + to: `"${user.name} <${user.email}>"`, + subject: "Redefinição de Senha - Laboratório de Dados Educacionais", + text + } + email(mailOptions, (err, info) => { + if(err) { + log.error(err); + res.json({msg: 'Undelivered Reset Password Mail'}); + } + log.info(`Message ${info.messageId} sent: ${info.response}`); + res.json({msg: 'Reset Password Mail Successfully Delivered'}); + }); + }) + } + }) +}) + +module.exports = userApp; diff --git a/src/libs/routes/verifyToken.js b/src/libs/routes/verifyToken.js new file mode 100644 index 0000000000000000000000000000000000000000..d54f64aa162c767c765784398dbcab455a9d666e --- /dev/null +++ b/src/libs/routes/verifyToken.js @@ -0,0 +1,52 @@ +const express = require('express'); + +const verifyTokenApp = express.Router(); + +const libs = `${process.cwd()}/libs`; + +const log = require(`${libs}/log`)(module); + +const VerificationToken = require(`${libs}/models/verificationToken`); + +const User = require(`${libs}/models/user`); + +verifyTokenApp.get('/:token', (req, res, next) => { + let token = req.params.token; + VerificationToken.findOne({token: token}, (err, vToken) => { + if(err) { + log.error(err); + return next(err); + } + if(!vToken) { + // TODO: generate new verification token + res.statusCode = 404; + return next({msg: 'Token not found', status:404}); + } + User.findById(vToken.userId, (err, user) => { + if(err) { + log.error(err); + next(err); + } + user.verified = true; + user.save((err) => { + if(err) { + log.error(err); + next(err); + } + }); + let u = user.toObject(); + delete u.salt; + delete u.hashedPassword; + vToken.verified = true; + vToken.save((err) => { + if(err) { + log.error(err); + next(err); + } + }); + res.json({msg: 'User verified', user: u}); + }); + }); +}); + +module.exports = verifyTokenApp; diff --git a/src/server.js b/src/server.js index 0eef8dfa5fb4764522f0b1c0be02da239603d347..889151c27370bdcce968e3b3c76689288405f689 100644 --- a/src/server.js +++ b/src/server.js @@ -1,23 +1,44 @@ -const debug = require('debug')('node-express-base'); - +const debug = require('debug')('simcaq-api'); const libs = `${process.cwd()}/libs`; - const config = require(`${libs}/config`); - const log = require(`${libs}/log`)(module); - const app = require(`${libs}/app`); - -process.env.NODE_ENV = process.env.NODE_ENV || 'development'; - -app.set('port', process.env.PORT || config.get('port') || 3000); - -// Set default ip: first environment variable IOP, then configuration and last '127.0.0.1' -app.set('ip', process.env.IP || config.get('ip') || '127.0.0.1'); - -const server = app.listen(app.get('port'), () => { - log.info(`Express server listening on port ${server.address().port}`); -}); - -// For testing -module.exports = server; +const compatVersion = require(`${libs}/middlewares/checkVersion`); +const cluster = require('cluster'); + +// Check if Node version is compatible +if (!compatVersion()) { + process.exit(1); +} + +if(cluster.isMaster) { + log.info(`Master ${process.pid} is running`); + + const numCPUs = (process.env.NODE_ENV != 'development') ? require('os').cpus().length : 1; + log.info(`Master will create ${numCPUs} workers`); + for(let i=0; i < numCPUs; ++i) { + cluster.fork(); + } + + // Caso uma instâcia morra + cluster.on('exit', (worker, code, signal) => { + log.info(`Worker ${worker.process.pid} died`); + // Revive a instância + cluster.fork(); + }); +} else { + // Set default port: first environment variable PORT, then configuration and last 3000 + app.set('port', process.env.PORT || config.port || 3000); + process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + // Set default ip: first environment variable IOP, then configuration and last '127.0.0.1' + app.set('ip', process.env.IP || config.ip || '127.0.0.1'); + + const server = app.listen(app.get('port'), () => { + log.info(`Express server listening on port ${server.address().port}`); + }); + + log.info(`Worker ${cluster.worker.id} is running (${process.pid})`); + + // For testing + module.exports = server; +} diff --git a/src/test/api.js b/src/test/api.js new file mode 100644 index 0000000000000000000000000000000000000000..b5476ea6352d086cb8d43f8e1f091686bc4f91c4 --- /dev/null +++ b/src/test/api.js @@ -0,0 +1,49 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); + +describe('API is running', () => { + it('should respond it\'s running', (done) => { + chai.request(server) + .get('/api/v1') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('msg'); + done(); + }) + }); + + it('should respond with 404 error', (done) => { + chai.request(server) + .get('/api/v1/thisrouteshouldgivea404') + .end((err, res) => { + res.should.have.status(404); + res.should.be.json; + res.body.should.have.property('error'); + done(); + }) + }); +}); diff --git a/src/test/city.js b/src/test/city.js new file mode 100644 index 0000000000000000000000000000000000000000..1ea61a62f5d31be3c2789df5bc577a137ea50afa --- /dev/null +++ b/src/test/city.js @@ -0,0 +1,84 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); + + +describe('request cities', () => { + it('should list all cities', (done) => { + chai.request(server) + .get('/api/v1/city') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list a city by id', (done) => { + chai.request(server) + .get('/api/v1/city?filter=id:4106902') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list all cities from a state', (done) => { + chai.request(server) + .get('/api/v1/city?filter=state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }) + }) + + it('should search for Curitiba', (done) => { + chai.request(server) + .get('/api/v1/city?search=name:curitiba') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); +}); diff --git a/src/test/class.js b/src/test/class.js new file mode 100644 index 0000000000000000000000000000000000000000..ef5254f45d847a568c17b31afafc8119273cb50e --- /dev/null +++ b/src/test/class.js @@ -0,0 +1,254 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request class', () => { + it('should list the locations', (done) => { + chai.request(server) + .get('/api/v1/class/location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/class/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should list the rural locations', (done) => { + chai.request(server) + .get('/api/v1/class/rural_location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education level', (done) => { + chai.request(server) + .get('/api/v1/class/education_level_mod') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education level short', (done) => { + chai.request(server) + .get('/api/v1/class/education_level_short') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the administrative dependencies', (done) => { + chai.request(server) + .get('/api/v1/class/adm_dependency_detailed') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the administrative dependencies detailed', (done) => { + chai.request(server) + .get('/api/v1/class/adm_dependency') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the periods', (done) => { + chai.request(server) + .get('/api/v1/class/period') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the periods', (done) => { + chai.request(server) + .get('/api/v1/class/integral_time') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list class', (done) => { + chai.request(server) + .get('/api/v1/class') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list class with valid filters', (done) => { + chai.request(server) + .get('/api/v1/class?filter=state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list class with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/class?dims=region,state,adm_dependency,location&filter=region:1') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('region_name'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('adm_dependency_name'); + res.body.result[0].should.have.property('location_name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list class with invalid dimensions', (done) => { + chai.request(server) + .get('/api/v1/class?dims=foo,bar') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list class with valid dimensions and filters', (done) => { + chai.request(server) + .get('/api/v1/class?dims=region,state,education_level_mod,school,period&filter=min_year:2015,max_year:2015,city:4106902') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('region_name'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('school_name'); + res.body.result[0].should.have.property('education_level_mod_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list class with dimension rural_location', (done) => { + chai.request(server) + .get('/api/v1/class?dims=rural_location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('rural_location_name'); + done(); + }); + }); + + it('should list enrollment with dimension education_level_short', (done) => { + chai.request(server) + .get('/api/v1/class?dims=education_level_short') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('education_level_short_name'); + done(); + }); + }); +}); diff --git a/src/test/classroom.js b/src/test/classroom.js new file mode 100644 index 0000000000000000000000000000000000000000..9a6fd00a18afe4f1c584bea4f033dd0dcb3a84a6 --- /dev/null +++ b/src/test/classroom.js @@ -0,0 +1,291 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request classrooms', () => { + + it('should list the locations', (done) => { + chai.request(server) + .get('/api/v1/classroom/location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the administrative dependencies', (done) => { + chai.request(server) + .get('/api/v1/classroom/adm_dependency') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the detailed administrative dependencies', (done) => { + chai.request(server) + .get('/api/v1/classroom/adm_dependency_detailed') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list classrooms', (done) => { + chai.request(server) + .get('/api/v1/classroom') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid filters', (done) => { + chai.request(server) + .get('/api/v1/classroom?filter=city:4106902') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid filters', (done) => { + chai.request(server) + .get('/api/v1/classroom?filter=state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with year range', (done) => { + chai.request(server) + .get('/api/v1/classroom/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list classrooms with year range', (done) => { + chai.request(server) + .get('/api/v1/classroom/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should list classrooms with valid filters', (done) => { + chai.request(server) + .get('/api/v1/classroom?filter=region:4') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid filters', (done) => { + chai.request(server) + .get('/api/v1/classroom?filter=adm_dependency:3') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid filters', (done) => { + chai.request(server) + .get('/api/v1/classroom?filter=adm_dependency_detailed:5') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid filters', (done) => { + chai.request(server) + .get('/api/v1/classroom?filter=location:1') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/classroom?dims=city') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('city_name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/classroom?dims=state') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/classroom?dims=region') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('region_name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/classroom?dims=adm_dependency') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('adm_dependency_name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/classroom?dims=adm_dependency_detailed') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('adm_dependency_detailed_name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/classroom?dims=location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list classrooms with invalid dimensions', (done) => { + chai.request(server) + .get('/api/v1/classroom?dims=foo,bar') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); +}); diff --git a/src/test/distributionFactor.js b/src/test/distributionFactor.js new file mode 100644 index 0000000000000000000000000000000000000000..e0531c6bfa03ee0d3aba2e49bb3c92e1f3510f13 --- /dev/null +++ b/src/test/distributionFactor.js @@ -0,0 +1,74 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request distribution factor', () => { + it('should list default distribution factor', (done) => { + chai.request(server) + .get('/api/v1/distribution_factor') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('city_id'); + res.body.result[0].should.have.property('series'); + res.body.result[0].series[0].should.have.property('serie_id'); + res.body.result[0].series[0].should.have.property('distribution_factor_reduction'); + done(); + }); + }); + + it('should list distribution factor with valid filters', (done) => { + chai.request(server) + .get('/api/v1/distribution_factor?filter=state:41,city:4100103') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('city_id'); + res.body.result[0].should.have.property('series'); + res.body.result[0].series[0].should.have.property('serie_id'); + done(); + }); + }); + + it('should list distribution factor with valid filters', (done) => { + chai.request(server) + .get('/api/v1/distribution_factor?filter=city:1100023') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('city_id'); + res.body.result[0].should.have.property('series'); + res.body.result[0].series[0].should.have.property('serie_id'); + res.body.result[0].series[0].should.have.property('distribution_factor_addition'); + done(); + }); + }); + +}); diff --git a/src/test/educationYears.js b/src/test/educationYears.js new file mode 100644 index 0000000000000000000000000000000000000000..7a47b3079c827bf9e47d1392e08522ce74874402 --- /dev/null +++ b/src/test/educationYears.js @@ -0,0 +1,43 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request education years', () => { + it('should list default education years', (done) => { + chai.request(server) + .get('/api/v1/education_years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('schoolYears'); + res.body.result[0].schoolYears.should.be.a('array'); + res.body.result[0].schoolYears[0].should.have.property('id'); + done(); + }); + }); +}); diff --git a/src/test/enrollment.js b/src/test/enrollment.js new file mode 100644 index 0000000000000000000000000000000000000000..997e5f70077c2d4b008845d33f452e0b7a9df679 --- /dev/null +++ b/src/test/enrollment.js @@ -0,0 +1,478 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request enrollments', () => { + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/enrollment/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/enrollment/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should list the years', (done) => { + chai.request(server) + .get('/api/v1/enrollment/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the locations', (done) => { + chai.request(server) + .get('/api/v1/enrollment/location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the rural locations', (done) => { + chai.request(server) + .get('/api/v1/enrollment/rural_location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the school year', (done) => { + chai.request(server) + .get('/api/v1/enrollment/school_year') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education level', (done) => { + chai.request(server) + .get('/api/v1/enrollment/education_level') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education level mod', (done) => { + chai.request(server) + .get('/api/v1/enrollment/education_level_mod') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education level short', (done) => { + chai.request(server) + .get('/api/v1/enrollment/education_level_short') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the administrative dependencies', (done) => { + chai.request(server) + .get('/api/v1/enrollment/adm_dependency') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the administrative dependencies detailed', (done) => { + chai.request(server) + .get('/api/v1/enrollment/adm_dependency_detailed') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list genders', (done) => { + chai.request(server) + .get('/api/v1/enrollment/gender') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the ethnic groups', (done) => { + chai.request(server) + .get('/api/v1/enrollment/ethnic_group') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the periods', (done) => { + chai.request(server) + .get('/api/v1/enrollment/period') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the integral time', (done) => { + chai.request(server) + .get('/api/v1/enrollment/integral_time') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list enrollments', (done) => { + chai.request(server) + .get('/api/v1/enrollment') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list enrollments with valid filters', (done) => { + chai.request(server) + .get('/api/v1/enrollment?filter=min_year:2014,state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list enrollments with invalid filters', (done) => { + chai.request(server) + .get('/api/v1/enrollment?filter=foo:2010,bar:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list enrollments with invalid dimensions', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=foo,bar') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list enrollments with multivalue filter and single value filter', (done) => { + chai.request(server) + .get('/api/v1/enrollment?filter=region:[1,2],min_year:2015') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list enrollments with valid dimensions and filters', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=region,state,school_year,school,gender,period&filter=min_year:2015,max_year:2015,city:4106902') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('region_name'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('school_name'); + res.body.result[0].should.have.property('school_year_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list enrollment with dimension location', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('location_name'); + done(); + }); + }); + + it('should list enrollment with dimension rural_location', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=rural_location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('rural_location_name'); + done(); + }); + }); + + it('should list enrollment with dimension school year', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=school_year') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('school_year_name'); + done(); + }); + }); + + it('should list enrollment with dimension education_level', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=education_level') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('education_level_name'); + done(); + }); + }); + + it('should list enrollment with dimension education_level_mod', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=education_level_mod') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('education_level_mod_name'); + done(); + }); + }); + + it('should list enrollment with dimension education_level_short', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=education_level_short') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('education_level_short_name'); + done(); + }); + }); + + it('should list enrollment with dimension adm_dependency', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=adm_dependency') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('adm_dependency_name'); + done(); + }); + }); + + it('should list enrollment with dimension adm_dependency_detailed', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=adm_dependency_detailed') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('adm_dependency_detailed_name'); + done(); + }); + }); + + it('should list enrollment with dimension gender', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=gender') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('gender_name'); + done(); + }); + }); + + it('should list enrollment with dimension ethnic_group', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=ethnic_group') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('ethnic_group_name'); + done(); + }); + }); + + it('should list enrollment with dimension period', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=period') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('period_name'); + done(); + }); + }); + + it('should list enrollment with dimension integral_time', (done) => { + chai.request(server) + .get('/api/v1/enrollment?dims=integral_time') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('integral_time_name'); + done(); + }); + }); +}); diff --git a/src/test/id2str.js b/src/test/id2str.js new file mode 100644 index 0000000000000000000000000000000000000000..153768988421d52127e8ff7f3f9a9b0d5800d4d2 --- /dev/null +++ b/src/test/id2str.js @@ -0,0 +1,100 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +const id2str = require(`${libs}/middlewares/id2str`); + +chai.use(chaiHttp); + +describe('id2str middleware', () => { + let req, res; + it('should return a function', (done) => { + expect(id2str.transform).to.be.a.Function; + done(); + }); + + it('should transform a gender id', (done) => { + expect(id2str.gender(1)).to.deep.equal('Masculino'); + done(); + }); + + it('should transform a period id', (done) => { + expect(id2str.period(1)).to.deep.equal('Matutino'); + done(); + }); + + it('should transform a location id', (done) => { + expect(id2str.location(6)).to.deep.equal('Unidade de uso sustentável'); + expect(id2str.location(4)).to.deep.equal('Terra indígena'); + expect(id2str.location(5)).to.deep.equal('Área remanescente de quilombos'); + done(); + }); + + it('should transform a school year id', (done) => { + expect(id2str.schoolYear(11)).to.deep.equal('Creche - Menor de 1 ano'); + done(); + }); + + + it('should transform a income level id', (done) => { + expect(id2str.incomeLevel(1)).to.deep.equal('1º quintil – 20% menores'); + done(); + }); + + it('should transform a class adm dependency id', (done) => { + expect(id2str.admDependency(6)).to.deep.equal('Não classificada'); + done(); + }); + + it('should transform a class adm dependency priv id', (done) => { + expect(id2str.admDependencyPriv(7)).to.deep.equal('Não classificada'); + + done(); + }); + + it('should transform a result', (done) => { + let req = { + result: [{gender_id: 2, period_id: 3, school_year_id: 11}] + }; + id2str.transform(false)(req, {}, (error)=> { + if (error) { throw new Error('Expected not to receive an error'); } + req.should.have.property('result'); + req.result.should.not.be.undefined; + req.result.should.be.deep.equal([{gender_id: 2, period_id: 3, school_year_id: 11, gender_name: 'Feminino', period_name: 'Noturno', school_year_name: 'Creche - Menor de 1 ano'}]); + done(); + }); + }); + + it('should transform a result and delete the ids', (done) => { + let req = { + result: [{gender_id: 2, period_id: 3, school_year_id: 11}] + }; + id2str.transform(true)(req, {}, (error)=>{ + if (error) { throw new Error('Expected not to receive an error'); } + req.should.have.property('result'); + req.result.should.not.be.undefined; + req.result.should.be.deep.equal([{gender_name: 'Feminino', period_name: 'Noturno', school_year_name: 'Creche - Menor de 1 ano'}]); + done(); + }); + }); +}); diff --git a/src/test/idhm.js b/src/test/idhm.js new file mode 100644 index 0000000000000000000000000000000000000000..cea8893e9a4bbe92b6028615f3c8c841d9abdca1 --- /dev/null +++ b/src/test/idhm.js @@ -0,0 +1,194 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request idhm', () => { + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/idhm/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); + + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/idhm/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the idhm levels', (done) => { + chai.request(server) + .get('/api/v1/idhm/idhm_level') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/idhm/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should return 400 with no filters', function (done) { + chai.request(server).get('/api/v1/idhm').end(function (err, res) { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }); + }); + + it('should list idhm with valid filters', (done) => { + chai.request(server) + .get('/api/v1/idhm?filter=min_year:2000,state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('state_name'); + done(); + }); + }); + + it('should list idhm with valid dims', (done) => { + chai.request(server) + .get('/api/v1/idhm?dims=city') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('city_id'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('city_name'); + res.body.result[0].should.have.property('state_name'); + done(); + }); + }); + + it('should list idhm with valid dims', (done) => { + chai.request(server) + .get('/api/v1/idhm?dims=state') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('state_name'); + done(); + }); + }); + + it('should list idhm with valid filtes and dims', (done) => { + chai.request(server) + .get('/api/v1/idhm?filter=state:41&dims=idhm_level') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('idhm_level_id'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('idhm_level_name'); + done(); + }) + }); + + it('should list idhm with valid filtes and dims', (done) => { + chai.request(server) + .get('/api/v1/idhm?filter=city:4100202') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('city_id'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('state_name'); + done(); + }) + }); + + it('should list idhm with valid filtes and dims', (done) => { + chai.request(server) + .get('/api/v1/idhm?dims=idhm_level,state') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('idhm_level_name'); + res.body.result[0].should.have.property('idhm_level_id'); + done(); + }) + }); + +}); diff --git a/src/test/idhme.js b/src/test/idhme.js new file mode 100644 index 0000000000000000000000000000000000000000..dc338d38adbbb0ce8362a0a9f1e950671b49295b --- /dev/null +++ b/src/test/idhme.js @@ -0,0 +1,138 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request idhme', () => { + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/idhme/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); + + it('should list the available years', (done) => { + chai.request(server) + .get('/api/v1/idhme/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/idhme/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should list idhme with valid filters', (done) => { + chai.request(server) + .get('/api/v1/idhme?filter=min_year:2000,state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + done(); + }); + }); + + it('should list idhme with invalid filters', (done) => { + chai.request(server) + .get('/api/v1/idhme?filter=foo:2010,bar:41') + .end((err, res) => { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }); + }); + + it('should list idhme with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/idhme?dims=state&filter=min_year:2010') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('state_name'); + done(); + }); + }); + + it('should list idhme with valid filters and dimensions', (done) => { + chai.request(server) + .get('/api/v1/idhme?filter=state:41,min_year:2010&dims=city') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('city_id'); + res.body.result[0].should.have.property('city_name'); + done(); + }); + }); + + it('should return 400 with no filters', (done) => { + chai.request(server) + .get('/api/v1/idhme') + .end((err, res) => { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }); + }); + +}); diff --git a/src/test/idhml.js b/src/test/idhml.js new file mode 100644 index 0000000000000000000000000000000000000000..90312b0684f5b033d98e4f1b281a5e716a372cbd --- /dev/null +++ b/src/test/idhml.js @@ -0,0 +1,138 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request idhml', () => { + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/idhml/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); + + it('should list the available years', (done) => { + chai.request(server) + .get('/api/v1/idhml/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/idhml/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should list idhml with valid filters', (done) => { + chai.request(server) + .get('/api/v1/idhml?filter=min_year:2000,state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + done(); + }); + }); + + it('should list idhml with invalid filters', (done) => { + chai.request(server) + .get('/api/v1/idhml?filter=foo:2010,bar:41') + .end((err, res) => { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }); + }); + + it('should list idhml with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/idhml?dims=state&filter=min_year:2010') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('state_name'); + done(); + }); + }); + + it('should list idhml with valid filters and dimensions', (done) => { + chai.request(server) + .get('/api/v1/idhml?filter=state:41,min_year:2010&dims=city') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('city_id'); + res.body.result[0].should.have.property('city_name'); + done(); + }); + }); + + it('should return 400 with no filters', (done) => { + chai.request(server) + .get('/api/v1/idhml') + .end((err, res) => { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }); + }); + +}); diff --git a/src/test/idhmr.js b/src/test/idhmr.js new file mode 100644 index 0000000000000000000000000000000000000000..eb06b37d0da0e2c5b03cf245906a4edb4e3c035e --- /dev/null +++ b/src/test/idhmr.js @@ -0,0 +1,138 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request idhmr', () => { + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/idhmr/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/idhmr/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should list the available years', (done) => { + chai.request(server) + .get('/api/v1/idhmr/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list idhmr with valid filters', (done) => { + chai.request(server) + .get('/api/v1/idhmr?filter=min_year:2000,state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + done(); + }); + }); + + it('should list idhmr with invalid filters', (done) => { + chai.request(server) + .get('/api/v1/idhmr?filter=foo:2010,bar:41') + .end((err, res) => { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }); + }); + + it('should list idhmr with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/idhmr?dims=state&filter=min_year:2010') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('state_name'); + done(); + }); + }); + + it('should list idhmr with valid filters and dimensions', (done) => { + chai.request(server) + .get('/api/v1/idhmr?filter=state:41,min_year:2010&dims=city') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('city_id'); + res.body.result[0].should.have.property('city_name'); + done(); + }); + }); + + it('should return 400 with no filters', (done) => { + chai.request(server) + .get('/api/v1/idhmr') + .end((err, res) => { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }); + }); + +}); diff --git a/src/test/infrastructure.js b/src/test/infrastructure.js new file mode 100644 index 0000000000000000000000000000000000000000..c92fda1e1b4ae003beff20c202d852fa997eb115 --- /dev/null +++ b/src/test/infrastructure.js @@ -0,0 +1,122 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request infrastructure', () => { + it('should list default query infrastructure', (done) => { + chai.request(server) + .get('/api/v1/infrastructure') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('school_place'); + done(); + }); + }); + + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/infrastructure/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/infrastructure/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should list the rural locations', (done) => { + chai.request(server) + .get('/api/v1/infrastructure/rural_location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the locations', (done) => { + chai.request(server) + .get('/api/v1/infrastructure/location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/infrastructure/adm_dependency_detailed') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/infrastructure/adm_dependency') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + +}); diff --git a/src/test/pibpercapita.js b/src/test/pibpercapita.js new file mode 100644 index 0000000000000000000000000000000000000000..e044b1c0ad631c7f236a9a67b9a50c2a111863f8 --- /dev/null +++ b/src/test/pibpercapita.js @@ -0,0 +1,128 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request pibpercapita', () => { + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/pibpercapita/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); + + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/pibpercapita/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/pibpercapita/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }) + + it('should list the income level', (done) => { + chai.request(server) + .get('/api/v1/pibpercapita/income_level') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }) + + it('should list the pib to state', (done) => { + chai.request(server) + .get('/api/v1/pibpercapita?filter=state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the list pib to state and income level', (done) => { + chai.request(server) + .get('/api/v1/pibpercapita?filter=state:41&dims=income_level') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('income_level_name'); + res.body.result[0].should.have.property('income_level_id'); + done(); + }); + }); + + city:4102802 + it('should list the pib to city', (done) => { + chai.request(server) + .get('/api/v1/pibpercapita?filter=city:4102802') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + + done(); + }); + }); + +}); diff --git a/src/test/population.js b/src/test/population.js new file mode 100644 index 0000000000000000000000000000000000000000..37be404fceaa84eb4e1b6d6a763096b2d557c675 --- /dev/null +++ b/src/test/population.js @@ -0,0 +1,126 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request population', () => { + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/population/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); + + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/population/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list default population', (done) => { + chai.request(server) + .get('/api/v1/population') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list default population', (done) => { + chai.request(server) + .get('/api/v1/population/city_size') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list population with valid filters', (done) => { + chai.request(server) + .get('/api/v1/population?filter=state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list population with valid filters', (done) => { + chai.request(server) + .get('/api/v1/population?filter=city:4102802') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the city size dims', (done) => { + chai.request(server) + .get('/api/v1/population?dims=city_size') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('city_size_id'); + res.body.result[0].should.have.property('city_size_name'); + done(); + }); + }); + +}); diff --git a/src/test/query.js b/src/test/query.js new file mode 100644 index 0000000000000000000000000000000000000000..cad0102ff882c4eb2121cb99c804b3b8b84f0fa4 --- /dev/null +++ b/src/test/query.js @@ -0,0 +1,88 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +const query = require(`${libs}/middlewares/query`).query; + +const squel = require('squel'); + +chai.use(chaiHttp); + +describe('Query middleware', () => { + let req, res; + it('should return a function', (done) => { + expect(query).to.be.a.Function; + done(); + }); + + it('should accept three arguments', function(done) { + expect(query.length).to.equal(3); + done(); + }); + + it('should do a valid query', (done) => { + let req = { + sql: squel.select().field('1+2') + }; + let res = {}; + query(req, {}, (error)=>{ + if (error) { throw new Error('Expected not to receive an error'); } + req.should.have.property('result'); + req.result.should.not.be.undefined; + req.result[0].should.have.property('L2'); + req.result[0].L2.should.be.equal(3); + done(); + }); + }); + + it('should return an error with an invalid query (prepared statement error)', (done) => { + let req = { + sql: squel.select() + }; + let res = {}; + query(req, {}, (error)=>{ + if (error) { done();} + }); + }); + + it('should return an error with an invalid query (execution error)', (done) => { + let req = { + sql: squel.select().from('ibge_pnad').from('ibge_censo') + }; + let res = {}; + query(req, {}, (error)=>{ + if (error) { done();} + }); + }); + + it('should not return 404 with an empty query result', (done) => { + let req = { + sql: squel.select().field('*').from('regiao').where('id>6') + }; + let res = {}; + query(req, {}, (error)=>{ + req.should.have.property('result'); + done(); + }); + }); +}); diff --git a/src/test/rateSchool.js b/src/test/rateSchool.js new file mode 100644 index 0000000000000000000000000000000000000000..b029fa42e87e76aca2a65c95bd17400abcd4e40c --- /dev/null +++ b/src/test/rateSchool.js @@ -0,0 +1,247 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request rate_school', () => { + it('should return 400 with no filters', (done) => { + chai.request(server) + .get('/api/v1/rate_school') + .end((err, res) => { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }) + }); + + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/rate_school/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the source', (done) => { + chai.request(server) + .get('/api/v1/rate_school/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + done(); + }); + }); + + it('should list the locations', (done) => { + chai.request(server) + .get('/api/v1/rate_school/location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the ethnic groups', (done) => { + chai.request(server) + .get('/api/v1/rate_school/ethnic_group_pnad') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the age range', (done) => { + chai.request(server) + .get('/api/v1/rate_school/age_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the age range', (done) => { + chai.request(server) + .get('/api/v1/rate_school?dims=age_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('age_range_id'); + res.body.result[0].should.have.property('age_range_name'); + done(); + }); + }); + + it('should list the gender', (done) => { + chai.request(server) + .get('/api/v1/rate_school/gender') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + + it('should list the dimensions of ethnic groups', (done) => { + chai.request(server) + .get('/api/v1//rate_school?dims=ethnic_group_pnad&filter=min_year:"2015",max_year:"2015",age_range:["1"]') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('ethnic_group_pnad_id'); + res.body.result[0].should.have.property('ethnic_group_pnad_name'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('denominator'); + res.body.result[0].should.have.property('partial'); + done(); + }); + }); + + it('should list the dimensions of gender', (done) => { + chai.request(server) + .get('/api/v1//rate_school?dims=gender&filter=min_year:"2015",max_year:"2015",age_range:["1"]') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('gender_id'); + res.body.result[0].should.have.property('gender_name'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('denominator'); + res.body.result[0].should.have.property('partial'); + done(); + }); + }); + + it('should list fifth household income', (done) => { + chai.request(server) + .get('/api/v1//rate_school/fifth_household_income') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the dimensions of fifth housebold income', (done) => { + chai.request(server) + .get('/api/v1//rate_school?dims=fifth_household_income&filter=min_year:"2015",max_year:"2015",age_range:["1"]') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('fifth_household_income_id'); + res.body.result[0].should.have.property('fifth_household_income_name'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('denominator'); + res.body.result[0].should.have.property('partial'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list extremes household income', (done) => { + chai.request(server) + .get('/api/v1//rate_school/extremes_household_income') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the dimensions of extremes housebold income', (done) => { + chai.request(server) + .get('/api/v1//rate_school?dims=extremes_household_income&filter=min_year:"2015",max_year:"2015",age_range:["1"]') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('extremes_household_income_id'); + res.body.result[0].should.have.property('extremes_household_income_name'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('denominator'); + res.body.result[0].should.have.property('partial'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list year range', (done) => { + chai.request(server) + .get('/api/v1//rate_school/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); +}); diff --git a/src/test/region.js b/src/test/region.js new file mode 100644 index 0000000000000000000000000000000000000000..daf45685c7a5298523afdf3e95446a715442429a --- /dev/null +++ b/src/test/region.js @@ -0,0 +1,71 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); + +describe('request regions', () => { + it('should list all regions', (done) => { + chai.request(server) + .get('/api/v1/region') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list region by id', (done) => { + chai.request(server) + .get('/api/v1/region?filter=id:1') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result.should.have.length(1); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('shoul search for south region', (done) => { + chai.request(server) + .get('/api/v1/region?search=name:sul') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result.should.have.length(1); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); +}); diff --git a/src/test/response.js b/src/test/response.js new file mode 100644 index 0000000000000000000000000000000000000000..bdafc9d89eefa01966bc4171ebcbd373924479cf --- /dev/null +++ b/src/test/response.js @@ -0,0 +1,55 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); + + +describe('test response', () => { + it('should list all regions in json', (done) => { + chai.request(server) + .get('/api/v1/region') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + done(); + }); + }); + it('should list all regions in xml', (done) => { + chai.request(server) + .get('/api/v1/region?format=xml') + .end((err, res) => { + res.should.have.status(200); + res.should.be.xml; + done(); + }); + }); + it('should list all regions in csv', (done) => { + chai.request(server) + .get('/api/v1/region?format=csv') + .end((err, res) => { + res.should.have.status(200); + done(); + }); + }); +}); diff --git a/src/test/school.js b/src/test/school.js new file mode 100644 index 0000000000000000000000000000000000000000..266c542850095f1469d7952174af80e3a35d18e1 --- /dev/null +++ b/src/test/school.js @@ -0,0 +1,98 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); + +describe('request schools', () => { + it('should list a school by id', (done) => { + chai.request(server) + .get('/api/v1/school?filter=id:11000023') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('year'); + //res.body.result[0].should.have.property('nome_entidade'); + done(); + }); + }); + + it('should list a school by id', (done) => { + chai.request(server) + .get('/api/v1/school/source') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); + //res.body.result[0].should.have.property('nome_entidade'); + done(); + }); + }); + + it('should list all schools from a state', (done) => { + chai.request(server) + .get('/api/v1/school?filter=state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('year'); + //res.body.result[0].should.have.property('nome_entidade'); + done(); + }); + }); + + it('should list all schools from a city', (done) => { + chai.request(server) + .get('/api/v1/school?filter=city:4102802') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('year'); + //res.body.result[0].should.have.property('nome_entidade'); + done(); + }) + }); + + it('should return 400 with no filters', (done) => { + chai.request(server) + .get('/api/v1/school') + .end((err, res) => { + res.should.have.status(400); + res.should.be.json; + res.body.should.have.property('error'); + res.body.error.should.be.equal('Wrong/No filter specified'); + done(); + }) + }); +}); diff --git a/src/test/schoolCount.js b/src/test/schoolCount.js new file mode 100644 index 0000000000000000000000000000000000000000..305045894b410b0c4215ce2d3d4d85aa065860c2 --- /dev/null +++ b/src/test/schoolCount.js @@ -0,0 +1,317 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); + +describe('request schools count', () => { + it('should list the locations', (done) => { + chai.request(server) + .get('/api/v1/school/location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the administrative dependencies', (done) => { + chai.request(server) + .get('/api/v1/school/adm_dependency') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the administrative dependencies detailed', (done) => { + chai.request(server) + .get('/api/v1/school/adm_dependency_detailed') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the government agreement', (done) => { + chai.request(server) + .get('/api/v1/school/government_agreement') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the agreement', (done) => { + chai.request(server) + .get('/api/v1/school/agreement') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education day care child', (done) => { + chai.request(server) + .get('/api/v1/school/education_day_care_child') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education preschool child', (done) => { + chai.request(server) + .get('/api/v1/school/education_preschool_child') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education begin elementary school', (done) => { + chai.request(server) + .get('/api/v1/school/education_begin_elementary_school') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education middle school', (done) => { + chai.request(server) + .get('/api/v1/school/education_middle_school') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education professional', (done) => { + chai.request(server) + .get('/api/v1/school/education_professional') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list the education eja', (done) => { + chai.request(server) + .get('/api/v1/school/education_eja') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list school with valid dimensions and filters', (done) => { + chai.request(server) + .get('/api/v1/school/count?dims=location,adm_dependency,government_agreement') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('location_name'); + res.body.result[0].should.have.property('adm_dependency_name'); + res.body.result[0].should.have.property('government_agreement_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list school with valid dimensions and filters', (done) => { + chai.request(server) + .get('/api/v1/school/count?dims=region,state&filter=min_year:2015,max_year:2016,city:4106902') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('region_name'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list school with no argument dimensions and filters', (done) => { + chai.request(server) + .get('/api/v1/school/count') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list school with valid dimensions and filters of states', (done) => { + chai.request(server) + .get('/api/v1/school/count?dims=state,education_professional,education_eja&filter=min_year:2015,max_year:2016') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('education_professional_name'); + res.body.result[0].should.have.property('education_eja_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list school with valid dimensions and filters of states', (done) => { + chai.request(server) + .get('/api/v1/school/count?dims=state&filter=min_year:2015,max_year:2016') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list school with valid dimensions and filters of states that have no toilet inside building', (done) => { + chai.request(server) + .get('/api/v1/school/count?dims=state&filter=min_year:2015,max_year:2016,education_begin_elementary_school:0') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list school with valid dimensions and filters of states with energy and water', (done) => { + chai.request(server) + .get('/api/v1/school/count?dims=state&filter=min_year:2015,max_year:2016,government_agreement:1') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list school with dimension rural_location', (done) => { + chai.request(server) + .get('/api/v1/school/count?dims=location') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('location_name'); + done(); + }); + }); + + it('should list school with dimension agreement', (done) => { + chai.request(server) + .get('/api/v1/school/count?dims=agreement') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('agreement_name'); + done(); + }); + }); +}); diff --git a/src/test/simulation.js b/src/test/simulation.js new file mode 100644 index 0000000000000000000000000000000000000000..cd68d68048984fb7c23d22f20a64ce5fe3ab73f2 --- /dev/null +++ b/src/test/simulation.js @@ -0,0 +1,433 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +const mongoose = require('../libs/db/mongoose'); +const Simulation = require('../libs/models/simulation'); +const User = require('../libs/models/user'); + +chai.use(chaiHttp); + +describe('Requires a simulation', () => { + let newSimulation; + + beforeEach(() => { + Simulation.remove({}, (err) => { + console.log('Test collection purged'); + }); + }); + + // it('should create a new simulation', (done) => { + // chai.request(server) + // .post('/api/v1/simulation') + // .set('content-type', 'application/x-www-form-urlencoded') + // .set('x-apicache-bypass', 'true') + // .send({ name: 'test_entry' }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('id'); + // res.body.id.should.be.a('string'); + // Simulation.findById(res.body.id, (err, simulation) => { + // simulation.should.have.property('name'); + // simulation.name.should.be.a('string'); + // simulation.name.should.equal('test_entry'); + // done(); + // }); + // }); + // }); + // it('should not create a nameless simulation', (done) => { + // chai.request(server) + // .post('/api/v1/simulation') + // .set('content-type', 'application/x-www-form-urlencoded') + // .set('x-apicache-bypass', 'true') + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.not.have.property('id'); + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // Simulation.findById(res.body.id, (err, simulation) => { + // expect(simulation).to.not.exist; + // done(); + // }); + // }); + // }); + // it('should find an existing simulation', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .get(`/api/v1/simulation/${id}`) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('_id'); + // res.body._id.should.be.a('string'); + // res.body.should.have.property('name'); + // res.body._id.should.be.a('string'); + // done(); + // }); + // }); + // }); + // it('should not find an unexisting simulation', (done) => { + // newSimulation = new Simulation(); + // let id = newSimulation._id; + // chai.request(server) + // .get(`/api/v1/simulation/${id}`) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // done(); + // }); + // }); + // it('should update an existing simulation\'s location', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ location: 5 }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('id'); + // res.body.id.should.be.a('string'); + // Simulation.findById(res.body.id, (err, simulation) => { + // simulation.should.have.property('name'); + // simulation.name.should.be.a('string'); + // simulation.name.should.equal('test'); + // simulation.should.have.property('location'); + // simulation.location.should.be.a('number'); + // simulation.location.should.equal(5); + // done(); + // }); + // }); + // }); + // }); + // it('should update multiple fields on a single request', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ + // name: 'new_name', + // location: 5, + // time: 3, + // failure_rate: [0.1, 0.2, 0.3], + // goals_care: [0.3, 0.2, 0.1], + // goals_inclusion: [0.8, 0.9, 1] + // }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('id'); + // res.body.id.should.be.a('string'); + // Simulation.findById(res.body.id, (err, simulation) => { + // simulation.should.have.property('name'); + // simulation.name.should.be.a('string'); + // simulation.name.should.equal('new_name'); + // simulation.should.have.property('location'); + // simulation.location.should.be.a('number'); + // simulation.location.should.equal(5); + // simulation.should.have.property('time'); + // simulation.time.should.be.a('number'); + // simulation.time.should.equal(3); + // simulation.should.have.property('failure_rate'); + // simulation.failure_rate.should.be.a('array'); + // simulation.failure_rate.length.should.equal(3); + // simulation.should.have.property('goals_care'); + // simulation.goals_care.should.be.a('array'); + // simulation.goals_care.length.should.equal(3); + // simulation.should.have.property('goals_inclusion'); + // simulation.goals_inclusion.should.be.a('array'); + // simulation.goals_inclusion.length.should.equal(3); + // done(); + // }); + // }); + // }); + // }); + // it('should not update an unexisting simulation', (done) => { + // newSimulation = new Simulation(); + // let id = newSimulation._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ location: 5 }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // done(); + // }); + // }); + // it('should update an existing simulation\'s time', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ time: 5 }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('id'); + // res.body.id.should.be.a('string'); + // Simulation.findById(res.body.id, (err, simulation) => { + // simulation.should.have.property('name'); + // simulation.name.should.be.a('string'); + // simulation.should.have.property('time'); + // simulation.time.should.be.a('number'); + // simulation.time.should.equal(5); + // done(); + // }); + // }); + // }); + // }); + // it('should not change results for empty post requests', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // done(); + // }); + // }); + // }); + // it('should not update in case of invalid field', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ + // name: 'other_name', + // totally_not_valid_value_for_an_entry: 'not hacking this api', + // }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // Simulation.findById(id, (err, simulation) => { + // simulation.name.should.equal('test'); + // done(); + // }); + // }); + // }); + // }); + // it('should include consistent enrollment tables', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ + // time: 5, + // enrollments: "[[100, 150, 200, 250, 300]]", + // }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('id'); + // res.body.id.should.be.a('string'); + // Simulation.findById(res.body.id, (err, simulation) => { + // simulation.should.have.property('name'); + // simulation.name.should.be.a('string'); + // simulation.should.have.property('time'); + // simulation.time.should.be.a('number'); + // simulation.time.should.equal(5); + // done(); + // }); + // }); + // }); + // }); + // it('should not accept an invalid time', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ + // time: "I'm an inocent time entry, don't mind me", + // }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // }); + // done(); + // }); + // }); + // it('should not accept enrollments table different than provided time', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ + // time: 5, + // enrollments: "[[1,2,3]]", + // }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // done(); + // }); + // }); + // }); + // it('should not include arrays of non arrays as enrollments', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ + // time: 5, + // enrollments: "[\"Tomato\"]", + // }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // done(); + // }); + // }); + // }); + // it('should not accept non array enrollments', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ + // time: 5, + // enrollments: "Am I still wanted here?", + // }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // done(); + // }); + // }); + // }); + // it('should not accept an enrollment with anything other than a number', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .post(`/api/v1/simulation/${id}`) + // .send({ + // time: 5, + // enrollments: "[[1,2,\"malicious payload\",4,5]]", + // }) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // done(); + // }); + // }); + // }); + // it('should delete an entry', (done) => { + // newSimulation = new Simulation(); + // newSimulation.name = 'test'; + // newSimulation.save((err, sim) => { + // let id = sim._id; + // chai.request(server) + // .delete(`/api/v1/simulation/${id}`) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(true); + // done(); + // }); + // }); + // }); + // it('should not delete an unexisting entry', (done) => { + // let sim = new Simulation(); + // let id = sim._id; + // chai.request(server) + // .delete(`/api/v1/simulation/${id}`) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('success'); + // res.body.success.should.equal(false); + // done(); + // }); + // }); + + // it('should returns an array in simulation/time', (done) => { + // let max_time = 10; + // chai.request(server) + // .get(`/api/v1/simulation/time?max_time=${max_time}`) + // .end((err, res) => { + // res.should.have.status(200); + // res.should.be.json; + // res.body.should.have.property('result'); + // res.body.result.should.be.array; + // done(); + // }); + // }); + + // it('should return an error when no max_time is specified in simulation/time', (done) => { + // chai.request(server) + // .get(`/api/v1/simulation/time`) + // .end((err, res) => { + // res.should.have.status(400); + // res.should.be.json; + // res.body.should.have.property('error'); + // res.body.error.should.equal('Invalid value for mandatory parameter max_time'); + // done(); + // }); + // }); +}); diff --git a/src/test/siope.js b/src/test/siope.js new file mode 100644 index 0000000000000000000000000000000000000000..ac13ead5ba92f76c6294f18fb324b90ed52396fd --- /dev/null +++ b/src/test/siope.js @@ -0,0 +1,87 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); +describe('request siope', () => { + it('should list the years', (done) => { + chai.request(server) + .get('/api/v1/siope/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list default siope', (done) => { + chai.request(server) + .get('/api/v1/siope') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('impostos'); + done(); + }); + }); + + it('should list siope with valid filters', (done) => { + chai.request(server) + .get('/api/v1/siope?filter=state:41') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('fundeb'); + res.body.result[0].should.have.property('impostos'); + done(); + }); + }); + + it('should list siope with valid dimensions', (done) => { + chai.request(server) + .get('/api/v1/siope?dims=state') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('state_id'); + res.body.result[0].should.have.property('fundeb'); + res.body.result[0].should.have.property('impostos'); + res.body.result[0].should.have.property('state_name'); + done(); + }); + }); + +}); diff --git a/src/test/state.js b/src/test/state.js new file mode 100644 index 0000000000000000000000000000000000000000..5cea7a4f69d6169208367df71571ebefa541a13a --- /dev/null +++ b/src/test/state.js @@ -0,0 +1,105 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +chai.use(chaiHttp); + + +describe('request states', () => { + it('should list all states', (done) => { + chai.request(server) + .get('/api/v1/state') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('region_id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list a state by id', (done) => { + chai.request(server) + .get('/api/v1/state?filter=id:11') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result.should.have.length(1); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('region_id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list states by region id', (done) => { + chai.request(server) + .get('/api/v1/state?filter=region:1') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('region_id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should list states by region id', (done) => { + chai.request(server) + .get('/api/v1/state?filter=region:1,region:2') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('region_id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); + + it('should search for Paraná', (done) => { + chai.request(server) + .get('/api/v1/state?search=name:paran') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result.should.have.length(1); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('region_id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); +}); diff --git a/src/test/test.js b/src/test/teacher.js similarity index 54% rename from src/test/test.js rename to src/test/teacher.js index 066b7b77c9651dbde50b97cd6ab730c78053d27b..30eddee8a20e6654c2db5f595c44e37f99868a5f 100644 --- a/src/test/test.js +++ b/src/test/teacher.js @@ -23,38 +23,64 @@ const libs = `${process.cwd()}/libs`; const server = require(`${libs}/app`); chai.use(chaiHttp); +describe('request teachers', () => { + it('should list the year range', (done) => { + chai.request(server) + .get('/api/v1/teacher/year_range') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('start_year'); + res.body.result[0].should.have.property('end_year'); + done(); + }); + }); -describe('API is running', () => { - it('should respond it\'s running', (done) => { + it('should list the source', (done) => { chai.request(server) - .get('/api/v1') + .get('/api/v1/teacher/source') .end((err, res) => { res.should.have.status(200); res.should.be.json; - res.body.should.have.property('msg'); + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('source'); done(); - }) + }); }); -}); -describe('request enrollments', () => { - it('should list the year range', (done) => { + it('should list the locations', (done) => { chai.request(server) - .get('/api/v1/enrollment/year_range') + .get('/api/v1/teacher/location') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('start_year'); - res.body.result[0].should.have.property('end_year'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); done(); }); }); - it('should list the education level', (done) => { + it('should list the locations', (done) => { chai.request(server) - .get('/api/v1/enrollment/education_level') + .get('/api/v1/teacher/years') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('year'); + done(); + }); + }); + + it('should list the rural locations', (done) => { + chai.request(server) + .get('/api/v1/teacher/rural_location') .end((err, res) => { res.should.have.status(200); res.should.be.json; @@ -66,9 +92,9 @@ describe('request enrollments', () => { }); }); - it('should list the administrative dependencies', (done) => { + it('should list the education level mod', (done) => { chai.request(server) - .get('/api/v1/enrollment/adm_dependency ') + .get('/api/v1/teacher/education_level_mod') .end((err, res) => { res.should.have.status(200); res.should.be.json; @@ -80,318 +106,323 @@ describe('request enrollments', () => { }); }); - it('should list enrollments', (done) => { + it('should list the education level short', (done) => { chai.request(server) - .get('/api/v1/enrollment') + .get('/api/v1/teacher/education_level_short') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); res.body.result[0].should.have.property('name'); - res.body.result[0].should.have.property('total'); done(); }); }); - it('should list enrollments with valid filters', (done) => { + it('should list the education type', (done) => { chai.request(server) - .get('/api/v1/enrollment?filter=min_year:2010,state:41') + .get('/api/v1/teacher/education_type') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); res.body.result[0].should.have.property('name'); - res.body.result[0].should.have.property('total'); done(); }); }); - it('should list enrollments with invalid filters', (done) => { + it('should list the contract type', (done) => { chai.request(server) - .get('/api/v1/enrollment?filter=foo:2010,bar:41') + .get('/api/v1/teacher/contract_type') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); res.body.result[0].should.have.property('name'); - res.body.result[0].should.have.property('total'); done(); }); }); - it('should list enrollments with valid dimensions', (done) => { + it('should list the administrative dependencies', (done) => { chai.request(server) - .get('/api/v1/enrollment?dims=region,state,adm_dependency_id,location_id&filter=min_year:2014,region:4') + .get('/api/v1/teacher/adm_dependency') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('region_name'); - res.body.result[0].should.have.property('state_name'); - res.body.result[0].should.have.property('adm_dependency_name'); - res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); done(); }); }); - it('should list enrollments with invalid dimensions', (done) => { + it('should list the administrative dependencies detailed', (done) => { chai.request(server) - .get('/api/v1/enrollment?dims=foo,bar') + .get('/api/v1/teacher/adm_dependency_detailed') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); res.body.result[0].should.have.property('name'); - res.body.result[0].should.have.property('total'); done(); }); }); - it('should list enrollments with valid dimensions and filters', (done) => { + it('should list genders', (done) => { chai.request(server) - .get('/api/v1/enrollment?dims=region,state,education_level_id,school&filter=min_year:2013,max_year:2014,city:3287') + .get('/api/v1/teacher/gender') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('region_name'); - res.body.result[0].should.have.property('state_name'); - res.body.result[0].should.have.property('school_name'); - res.body.result[0].should.have.property('education_level'); - res.body.result[0].should.have.property('total'); - res.body.result[0].should.have.property('year'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); done(); }); }); + it('should list the ethnic groups', (done) => { + chai.request(server) + .get('/api/v1/teacher/ethnic_group') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('id'); + res.body.result[0].should.have.property('name'); + done(); + }); + }); -}); -describe('request regions', () => { - it('should list all regions', (done) => { + it('should list teachers count', (done) => { chai.request(server) - .get('/api/v1/region') + .get('/api/v1/teacher?filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_regiao_id'); - res.body.result[0].should.have.property('nome'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); done(); }); }); - it('should list region by id', (done) => { + it('should list teacher count with valid filters', (done) => { chai.request(server) - .get('/api/v1/region/1') + .get('/api/v1/teacher?filter=min_year:2015,max_year:2015,state:41') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result.should.have.length(1); - res.body.result[0].should.have.property('pk_regiao_id'); - res.body.result[0].should.have.property('nome'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); done(); }); }); -}); -describe('request states', () => { - it('should list all states', (done) => { + it('should list teacher count with invalid filters', (done) => { chai.request(server) - .get('/api/v1/state') + .get('/api/v1/teacher?filter=foo:2010,bar:41,min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_estado_id'); - res.body.result[0].should.have.property('fk_regiao_id'); - res.body.result[0].should.have.property('nome'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); done(); }); }); - it('should list a state by id', (done) => { + it('should list teacher count with valid dimensions', (done) => { chai.request(server) - .get('/api/v1/state/11') + .get('/api/v1/teacher?dims=region,state,adm_dependency,location,gender,ethnic_group&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result.should.have.length(1); - res.body.result[0].should.have.property('pk_estado_id'); - res.body.result[0].should.have.property('fk_regiao_id'); - res.body.result[0].should.have.property('nome'); + res.body.result[0].should.have.property('region_name'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('adm_dependency_name'); + res.body.result[0].should.have.property('location_name'); + res.body.result[0].should.have.property('total'); done(); }); }); - it('should list states by region id', (done) => { + it('should list teacher count with invalid dimensions', (done) => { chai.request(server) - .get('/api/v1/state/region/1') + .get('/api/v1/teacher?dims=foo,bar&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_estado_id'); - res.body.result[0].should.have.property('fk_regiao_id'); - res.body.result[0].should.have.property('nome'); + res.body.result[0].should.have.property('name'); + res.body.result[0].should.have.property('total'); + done(); + }); + }); + + it('should list teacher count with valid dimensions and filters', (done) => { + chai.request(server) + .get('/api/v1/teacher?dims=region,state,school,gender&filter=min_year:2015,max_year:2015,city:4106902') + .end((err, res) => { + res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('region_name'); + res.body.result[0].should.have.property('state_name'); + res.body.result[0].should.have.property('school_name'); + res.body.result[0].should.have.property('total'); + res.body.result[0].should.have.property('year'); done(); }); }); -}); -describe('request cities', () => { - it('should list all cities', (done) => { + it('should list teacher count with dimension location', (done) => { chai.request(server) - .get('/api/v1/city') + .get('/api/v1/teacher?dims=location&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_municipio_id'); - res.body.result[0].should.have.property('fk_estado_id'); - res.body.result[0].should.have.property('nome'); - res.body.result[0].should.have.property('codigo_ibge'); + res.body.result[0].should.have.property('location_name'); done(); }); }); - it('should list a city by id', (done) => { + it('should list teacher count with dimension rural_location', (done) => { chai.request(server) - .get('/api/v1/city/1') + .get('/api/v1/teacher?dims=rural_location&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_municipio_id'); - res.body.result[0].should.have.property('fk_estado_id'); - res.body.result[0].should.have.property('nome'); - res.body.result[0].should.have.property('codigo_ibge'); + res.body.result[0].should.have.property('rural_location_name'); done(); }); }); - it('should list a city by codigo_ibge', (done) => { + it('should list teacher count with dimension education_level_mod', (done) => { chai.request(server) - .get('/api/v1/city/ibge/1200013') + .get('/api/v1/teacher?dims=education_level_mod&filter=min_year:2015,max_year:2015') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_municipio_id'); - res.body.result[0].should.have.property('fk_estado_id'); - res.body.result[0].should.have.property('nome'); - res.body.result[0].should.have.property('codigo_ibge'); + res.body.result[0].should.have.property('education_level_mod_name'); done(); }); }); - it('should list all cities from a state', (done) => { + it('should list teacher with dimension education_level_short', (done) => { chai.request(server) - .get('/api/v1/city/state/41') + .get('/api/v1/teacher?dims=education_level_short&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_municipio_id'); - res.body.result[0].should.have.property('fk_estado_id'); - res.body.result[0].should.have.property('nome'); - res.body.result[0].should.have.property('codigo_ibge'); + res.body.result[0].should.have.property('education_level_short_name'); done(); - }) - }) -}); + }); + }); -describe('request schools', () => { - it('should list a school by id', (done) => { + it('should list teacher count with dimension education type', (done) => { chai.request(server) - .get('/api/v1/school/185588') + .get('/api/v1/teacher?dims=education_type&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_escola_id'); - res.body.result[0].should.have.property('ano_censo'); - res.body.result[0].should.have.property('cod_entidade'); - res.body.result[0].should.have.property('nome_entidade'); + res.body.result[0].should.have.property('education_type_name'); done(); }); }); - it('should list all schools from a state', (done) => { + it('should list teacher count with dimension adm_dependency', (done) => { chai.request(server) - .get('/api/v1/school/state/41') + .get('/api/v1/teacher?dims=adm_dependency&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_escola_id'); - res.body.result[0].should.have.property('ano_censo'); - res.body.result[0].should.have.property('nome_entidade'); + res.body.result[0].should.have.property('adm_dependency_name'); done(); }); }); - it('should list all schools from a city', (done) => { + it('should list teacher count with dimension adm_dependency_detailed', (done) => { chai.request(server) - .get('/api/v1/school/city/3287') + .get('/api/v1/teacher?dims=adm_dependency_detailed&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; res.body.should.have.property('result'); res.body.result.should.be.a('array'); - res.body.result[0].should.have.property('pk_escola_id'); - res.body.result[0].should.have.property('ano_censo'); - res.body.result[0].should.have.property('nome_entidade'); + res.body.result[0].should.have.property('adm_dependency_detailed_name'); done(); - }) - }) -}); + }); + }); -describe('test response', () => { - it('should list all regions in json', (done) => { + it('should list teacher count with dimension gender', (done) => { chai.request(server) - .get('/api/v1/region') + .get('/api/v1/teacher?dims=gender&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('gender_name'); done(); }); }); - it('should list all regions in xml', (done) => { + it('should list teacher count with dimension ethnic_group', (done) => { chai.request(server) - .get('/api/v1/region?format=xml') + .get('/api/v1/teacher?dims=ethnic_group&filter=min_year:2016,max_year:2016') .end((err, res) => { res.should.have.status(200); - res.should.be.xml; + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('ethnic_group_name'); done(); }); }); - it('should list all regions in csv', (done) => { + + + it('should list teacher count with dimension contract type', (done) => { chai.request(server) - .get('/api/v1/region?format=csv') + .get('/api/v1/teacher?filter=state:41&dims=contract_type') .end((err, res) => { res.should.have.status(200); + res.should.be.json; + res.body.should.have.property('result'); + res.body.result.should.be.a('array'); + res.body.result[0].should.have.property('contract_type_name'); done(); }); }); diff --git a/src/test/user.js b/src/test/user.js new file mode 100644 index 0000000000000000000000000000000000000000..3109960bd98dfec2727c5ba7bf87b28148046ae7 --- /dev/null +++ b/src/test/user.js @@ -0,0 +1,37 @@ +process.env.NODE_ENV = 'test'; + +const chai = require('chai'); + +const dirtyChai = require('dirty-chai'); + +chai.use(dirtyChai); + +const chaiXml = require('chai-xml'); + +chai.use(chaiXml); + +const chaiHttp = require('chai-http'); + +const assert = chai.assert; + +const expect = chai.expect; + +const should = chai.should(); // actually call the function + +const libs = `${process.cwd()}/libs`; + +const server = require(`${libs}/app`); + +const mongoose = require('../libs/db/mongoose'); +//const Simulation = require('../libs/models/simulation'); +const User = require('../libs/models/user'); + +chai.use(chaiHttp); + +describe('Saves a user', () => { + // beforeEach(() => { + // User.remove({}, (err) => { + // console.log('Test collection purged') + // }); + // }); +});