diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 04cdb1f9f46441a3617636eea51bf6403f1e2ab9..90d49bbe2acae1aa3d36e0b4a42ae3cdf0ead3e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,6 +18,7 @@ run_tests: - ping -W1 -c1 mongo - mv config.json.example config.json - sed -i -e 's/false/true/g' config.json + - sed -i -e 's/simcaq_dev/simcaq_dev2/g' config.json - gulp build - gulp test tags: diff --git a/gulpfile.babel.js b/gulpfile.babel.js index e75f65bf5208dacdfb44e4a94fe77ffbfea0a262..11e9416f87c8528c84d0bbb64f5312c8455215b4 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -75,7 +75,7 @@ gulp.task('pre-test', () => { gulp.task('test', ['pre-test'], () => { process.chdir('build'); gulp.src(['test/**/*.js'], {read: false}) - .pipe(mocha({timeout: 15000})) + .pipe(mocha({timeout: 60000})) .pipe(istanbul.writeReports()) .pipe(istanbul.enforceThresholds({ thresholds: { @@ -84,7 +84,7 @@ gulp.task('test', ['pre-test'], () => { branches: 70, lines: 80, functions: 80 - } + } } })) .on('error', () => { @@ -115,4 +115,4 @@ gulp.task('run', () => { }); }); -gulp.task('default', ['run']); \ No newline at end of file +gulp.task('default', ['run']); diff --git a/src/libs/db/query_exec.js b/src/libs/db/query_exec.js index 1a2f7387574bc2f886550ef39f86f221c6a98e49..c43b1dbee3640a71e1957db722e6eab84e1d8533 100644 --- a/src/libs/db/query_exec.js +++ b/src/libs/db/query_exec.js @@ -22,7 +22,6 @@ function execSqlQuery(sqlQuery, sqlQueryParams = []) { conn.prepare(sqlQuery, true).then((dbQuery) => { // Execute query dbQuery.exec(sqlQueryParams).then((dbResult) => { - log.debug(`Query result: ${dbResult.data}`); // release resources allocated for the prepared statement dbQuery.release(); resolve(dbResult.data); diff --git a/src/libs/middlewares/reqQueryFields.js b/src/libs/middlewares/reqQueryFields.js index 1fe597d38f2cd97e7ea5c96b4ff4c5ae96eb97e1..6a194c845357c189b860f6b3fee4838f0f9ffdbe 100644 --- a/src/libs/middlewares/reqQueryFields.js +++ b/src/libs/middlewares/reqQueryFields.js @@ -113,15 +113,11 @@ class ReqQueryFields { let params = []; // f é o campo let f = this.fields[key]; - log.debug('f'); - log.debug(f); // 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]; - log.debug('value'); - log.debug(value); // Pushamos o parametro params.push(value.name); }); @@ -186,25 +182,29 @@ class ReqQueryFields { // Fazemos um foreach nos parametros dentro do atributo Object.keys(param).forEach((k) => { let values = _.merge(this.fieldValues, field.values); - // log.debug('ValueS'); - // log.debug(values); - // log.debug('k'); - // log.debug(k); if(typeof values[k] !== 'undefined') { // Clonamos para não alterar o original let value = _.clone(values[k]); - // log.debug('value'); - // log.debug(value); - // log.debug(hasJoined); // Checa se não fizemos o join para este valor e se é necessário fazer - if(!hasJoined[value.name] && typeof value.join !== 'undefined') { + if(!hasJoined[value.table] && typeof value.join !== 'undefined') { let foreignTable = ''; if(value.join.foreignTable) foreignTable = value.join.foreignTable+'.'; // Fazemos o join - req.sql.join(value.table, null, foreignTable+value.join.foreign+'='+value.table+'.'+value.join.primary); + 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; + } + req.sql.join(value.table, null, onClause); // Marcamos o join como feito para não ter problemas - hasJoined[value.name] = true; - // values[k].hasJoined = true; + hasJoined[value.table] = true; } // Se o valor é um campo a ser incluÃdo no SELECT if(typeof field.field !== 'undefined' && field.field) { diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js index 2c29a24680987a9b6c572cf5fa4ba86bc0b0c83c..ce905d8bd1f8476c38795a03a78347c85ed9cda3 100644 --- a/src/libs/routes/api.js +++ b/src/libs/routes/api.js @@ -18,7 +18,7 @@ const city = require('./city'); const school = require('./school'); -const location = require('./location'); +const spatial = require('./spatial'); const simulation = require('./simulation'); @@ -36,6 +36,6 @@ api.use('/state', cache('15 day'), state); api.use('/region', cache('15 day'), region); api.use('/city', cache('15 day'), city); api.use('/school', cache('15 day'), school); -api.use('/location', cache('1 day'), location); +api.use('/spatial', cache('1 day'), spatial); module.exports = api; diff --git a/src/libs/routes/city.js b/src/libs/routes/city.js index 3b03ef9c3899450503aff7eeb7ffcf087104034c..b7999a8ef9e35fa7fd8011ae73c2149f2d4d02e3 100644 --- a/src/libs/routes/city.js +++ b/src/libs/routes/city.js @@ -21,11 +21,11 @@ rqf.addField({ }).addValue({ name: 'id', table: 'municipio', - tableField: 'pk_cod_ibge', + tableField: 'id', where: { relation: '=', type: 'integer', - field: 'pk_cod_ibge' + field: 'id' } }).addValue({ name: 'state', @@ -35,12 +35,12 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'fk_estado_id', + field: 'estado_id', table: 'municipio' }, join: { - primary: 'pk_estado_id', - foreign: 'fk_estado_id', + primary: 'id', + foreign: 'estado_id', foreignTable: 'municipio' } }).addField({ @@ -62,8 +62,8 @@ rqf.addField({ cityApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { req.sql.from('municipio') .field('municipio.nome', 'name') - .field('municipio.pk_cod_ibge', 'id') - .field('municipio.fk_estado_id', 'state_id'); + .field('municipio.id') + .field('municipio.estado_id', 'state_id'); next(); }, query, response('city')); diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js index 74ba538adc9d010a2c3ed8f748a9f8cf90996030..e8664c4f98b8190df1bb65f856ee9ca64f32856d 100644 --- a/src/libs/routes/enrollment.js +++ b/src/libs/routes/enrollment.js @@ -19,16 +19,15 @@ 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.from('turma') - .field('MIN(turma.ano_censo)', 'start_year') - .field('MAX(turma.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')); enrollmentApp.get('/location', (req, res, next) => { req.sql = squel.select() - .field('pk_localizacao_id', 'id') + .field('id') .field('descricao', 'name') .from('localizacao'); next(); @@ -36,21 +35,51 @@ enrollmentApp.get('/location', (req, res, next) => { // Returns all educational levels avaible enrollmentApp.get('/education_level', (req, res, next) => { - req.sql.from('etapa_ensino') - .field('pk_etapa_ensino_id', 'id') - .field('desc_etapa', 'name'); - + req.sql.from('serie_ano') + .field('id') + .field('nome', 'name'); next(); }, query, response('education_level')); // Returns all adm dependencies enrollmentApp.get('/adm_dependency', (req, res, next) => { req.sql.from('dependencia_adm') - .field('pk_dependencia_adm_id', 'id') + .field('id') + .field('nome', 'name') + .where('id <= 4'); + next(); +}, query, response('adm_dependency')); + +enrollmentApp.get('/adm_dependency_detailed', (req, res, next) => { + req.sql.from('dependencia_adm') + .field('id', 'id') .field('nome', 'name'); + next(); +}, query, response('adm_dependency_detailed')); +// Return genders +enrollmentApp.get('/gender', (req, res, next) => { + req.result = [ + {id: 1, name: 'Masculino'}, + {id: 2, name: 'Feminino'} + ]; next(); -}, query, response('adm_dependency')); +}, response('gender')); + +// Return ethnic group +enrollmentApp.get('/ethnic_group', (req, res, next) => { + req.sql.from('cor_raca') + .field('id') + .field('nome', 'name'); + next(); +}, query, response('ethnic_group')); + +enrollmentApp.get('/period', (req, res, next) => { + req.sql.from('turma_turno') + .field('id') + .field('nome', 'name'); + next(); +}, query, response('period')); rqf.addField({ name: 'filter', @@ -68,27 +97,42 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'pk_dependencia_adm_id' + field: 'id' }, join: { - primary: 'pk_dependencia_adm_id', - foreign: 'fk_dependencia_adm_id', - foreignTable: 'turma' + primary: 'id', + foreign: 'dependencia_adm_id', + foreignTable: 'matricula' + } +}).addValue({ + name: 'adm_dependency_detailed', + table: 'dependencia_adm', + tableField: 'nome', + resultField: 'adm_dependency_detailed_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'dependencia_adm_priv', + foreignTable: 'matricula' } }).addValue({ name: 'education_level', - table: 'etapa_ensino', - tableField: 'desc_etapa', - resultField: 'education_level', + table: 'serie_ano', + tableField: 'nome', + resultField: 'education_level_name', where: { relation: '=', type: 'integer', - field: 'pk_etapa_ensino_id' + field: 'id' }, join: { - primary: 'pk_etapa_ensino_id', - foreign: 'fk_etapa_ensino_id', - foreignTable: 'turma' + primary: 'id', + foreign: 'serie_ano_id', + foreignTable: 'matricula' } }).addValue({ name: 'region', @@ -98,12 +142,12 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'pk_regiao_id' + field: 'id' }, join: { - primary: 'pk_regiao_id', - foreign: 'fk_regiao_id', - foreignTable: 'turma' + primary: 'id', + foreign: 'regiao_id', + foreignTable: 'matricula' } }).addValue({ name: 'state', @@ -113,12 +157,12 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'pk_estado_id' + field: 'id' }, join: { - primary: 'pk_estado_id', - foreign: 'fk_estado_id', - foreignTable: 'turma' + primary: 'id', + foreign: 'escola_estado_id', + foreignTable: 'matricula' } }).addValue({ name: 'city', @@ -128,27 +172,27 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'pk_cod_ibge' + field: 'id' }, join: { - primary: 'pk_cod_ibge', - foreign: 'fk_municipio_id', - foreignTable: 'turma' + primary: 'id', + foreign: 'escola_municipio_id', + foreignTable: 'matricula' } }).addValue({ name: 'school', table: 'escola', - tableField: 'cod_entidade', + tableField: 'nome_escola', resultField: 'school_name', where: { relation: '=', type: 'integer', - field: 'cod_entidade' + field: 'id' }, join: { - primary: 'cod_entidade', - foreign: 'cod_entidade', - foreignTable: 'turma' + primary: ['id', 'ano_censo'], + foreign: ['escola_id', 'ano_censo'], + foreignTable: 'matricula' } }).addValue({ name: 'location', @@ -158,31 +202,16 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'pk_localizacao_id' - }, - join: { - primary: 'pk_localizacao_id', - foreign: 'fk_localizacao_id', - foreignTable: 'turma' - } -}).addValue({ - name: 'city', - table: 'municipio', - tableField: 'nome', - resultField: 'city_name', - where: { - relation: '=', - type: 'integer', - field: 'pk_cod_ibge' + field: 'id' }, join: { - primary: 'pk_cod_ibge', - foreign: 'fk_municipio_id', - foreignTable: 'turma' + primary: 'id', + foreign: 'localizacao_id', + foreignTable: 'matricula' } }).addValue({ name: 'min_year', - table: 'turma', + table: 'matricula', tableField: 'ano_censo', resultField: 'year', where: { @@ -192,7 +221,7 @@ rqf.addField({ } }).addValue({ name: 'max_year', - table: 'turma', + table: 'matricula', tableField: 'ano_censo', resultField: 'year', where: { @@ -200,17 +229,91 @@ rqf.addField({ type: 'integer', field: 'ano_censo' } +}).addValue({ + name: 'gender', + table: 'matricula', + tableField: 'sexo', + resultField: 'gender_id', + where: { + relation: '=', + type: 'integer', + field: 'sexo' + } +}).addValue({ + name: 'ethnic_group', + table: 'cor_raca', + tableField: 'nome', + resultField: 'ethnic_group_name', + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'cor_raca_id', + foreignTable: 'matricula' + } +}).addValue({ + name: 'period', + table: 'turma', + tableField: 'turno', + resultField: 'period_id', + where: { + relation: '=', + type: 'integer', + field: 'turno' + }, + join: { + primary: 'id', + foreign: 'turma_id', + foreignTable: 'matricula' + } }); enrollmentApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { log.debug(req.sql.toParam()); - req.sql.field('COALESCE(SUM(num_matriculas), 0)', 'total') + req.sql.field('COALESCE(COUNT(matricula.id), 0)', 'total') .field("'Brasil'", 'name') - .field('turma.ano_censo', 'year') - .from('turma') - .group('turma.ano_censo') - .order('turma.ano_censo'); + .field('matricula.ano_censo', 'year') + .from('matricula') + .group('matricula.ano_censo') + .order('matricula.ano_censo') + .where('matricula.tipo=0 OR matricula.tipo=1 OR matricula.tipo=2 OR matricula.tipo=3'); + next(); +}, query, (req, res, next) => { + // ids to strings + req.result.forEach((result) => { + if(typeof result.gender_id !== 'undefined') { + switch (result.gender_id) { + case 1: + result.gender_name = 'Masculino'; + break; + case 2: + result.gender_name = 'Feminino'; + break; + } + delete result.gender_id; + } + if(typeof result.period_id !== 'undefined') { + switch (result.period_id) { + case 1: + result.period_name = 'Diurno'; + break; + case 2: + result.period_name = 'Noturno'; + break; + case 3: + result.period_name = 'Integral'; + break; + default: + result.period_name = 'Indefinido'; + break; + } + delete result.period_id; + } + }); next(); -}, query, response('enrollment')); +}, response('enrollment')); module.exports = enrollmentApp; diff --git a/src/libs/routes/location.js b/src/libs/routes/location.js deleted file mode 100644 index f5476f5ea451876aa2e4c6ad0d4fe67a5b214879..0000000000000000000000000000000000000000 --- a/src/libs/routes/location.js +++ /dev/null @@ -1,1279 +0,0 @@ -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`); - -const sqlQuery = require(`${libs}/db/query_exec`); - -const response = require(`${libs}/middlewares/response`); - -const locationApp = express(); - -function locationIdToStr(locationId) { - let locationStr = 'Total'; - switch(locationId) { - case 1: - locationStr = 'Urbana'; break; - case 2: - locationStr = 'Rural'; break; - case 3: - locationStr = 'Ãrea de assentamento'; break; - case 4: - locationStr = 'Terra indÃgena'; break; - case 5: - locationStr = 'Ãrea remanescente de quilombos'; break; - case 6: - locationStr = 'Unidade de uso sustentável'; break; - - } - return locationStr; -} - -function schoolYearIdToStr(schoolYearId) -{ - let schoolYearStr; - switch(schoolYearId) { - case 11: - schoolYearStr = 'Creche'; - break; - case 21: - schoolYearStr = 'Pré-escola'; - break; - case 31: - schoolYearStr = '1 Ano'; - break; - case 32: - schoolYearStr = '2 Ano - 1 Serie'; - break; - case 33: - schoolYearStr = '3 Ano - 2 Serie'; - break; - case 34: - schoolYearStr = '4 Ano - 3 Serie'; - break; - case 35: - schoolYearStr = '5 Ano - 4 Serie'; - break; - case 41: - schoolYearStr = '6 Ano - 5 Serie'; - break; - case 42: - schoolYearStr = '7 Ano - 6 Serie'; - break; - case 43: - schoolYearStr = '8 Ano - 7 Serie'; - break; - case 44: - schoolYearStr = '9 Ano - 8 Serie'; - break; - case 51: - schoolYearStr = '1 Ano'; // equivalent to 'EM 1 Série' - break; - case 52: - schoolYearStr = '2 Ano'; // equivalent to 'EM 2 Série' - break; - case 53: - schoolYearStr = '3 Ano'; // equivalent to 'EM 3 Série' - break; - case 54: - schoolYearStr = '4 Ano'; // equivalent to 'EM 4 Série' - break; - case 61: - schoolYearStr = 'EJA AI'; - break; - case 62: - schoolYearStr = 'EJA AF'; - break; - case 63: - schoolYearStr = 'EJA EM'; - break; - case 64: - schoolYearStr = 'EJA semi-presencial'; - break; - case 71: - schoolYearStr = 'EP'; - break; - case 81: - schoolYearStr = 'Atividades complementares e AEE'; - break; - default: - schoolYearStr = 'Não classificado'; - } - return schoolYearStr; -} - -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) { - log.debug(row); - resultMap[resultLbl].push(row); - } - } - resultIdx++; - } - log.debug(resultMap); - 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()); }); -} - -locationApp.get('/sociodemographic', (req, res, next) => { - const populationYearQry = squel.select() - .field('MAX(ibge_populacao.ano_censo)') - .from('ibge_populacao'); - - const populationQry = squel.select() - .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 = squel.select() - .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 = squel.select() - .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 = squel.select() - .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 = squel.select() - .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('location')); - -locationApp.get('/sociodemographic/region/:id', (req, res, next) => { - const regionId = parseInt(req.params.id, 10); - - const populationYearQry = squel.select() - .field('MAX(ibge_populacao.ano_censo)') - .from('ibge_populacao'); - - const populationQry = squel.select() - .field('regiao.nome', 'name') - .field('SUM(populacao)', 'population') - .field('ano_censo', 'census_year') - .from('ibge_populacao') - .from('municipio') - .from('estado') - .from('regiao') - .where(`regiao.pk_regiao_id = ${regionId}`) - .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`) - .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .where('estado.fk_regiao_id = regiao.pk_regiao_id') - .group('regiao.nome') - .group('ibge_populacao.ano_censo') - .order('regiao.nome'); - - const pibYearQry = squel.select() - .field('MAX(ibge_pib.ano_censo)') - .from('ibge_pib'); - - const pibQry = squel.select() - .field('regiao.nome', 'name') - .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita') - .field('ano_censo', 'census_year') - .from('ibge_pib') - .from('municipio') - .from('estado') - .from('regiao') - .where(`regiao.pk_regiao_id = ${regionId}`) - .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`) - .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .where('estado.fk_regiao_id = regiao.pk_regiao_id') - .group('regiao.nome') - .group('ibge_pib.ano_censo') - .order('regiao.nome'); - - const idhYearQry = squel.select() - .field('MAX(adh_idh.ano_censo)') - .from('adh_idh'); - - const idhQry = squel.select() - .field('regiao.nome', 'name') - .field('AVG(idhm)', 'idhm') - .field('ano_censo', 'census_year') - .from('adh_idh') - .from('municipio') - .from('estado') - .from('regiao') - .where(`regiao.pk_regiao_id = ${regionId}`) - .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`) - .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .where('estado.fk_regiao_id = regiao.pk_regiao_id') - .group('regiao.nome') - .group('adh_idh.ano_censo') - .order('regiao.nome'); - - const analfabYearQry = squel.select() - .field('MAX(adh_analfabetismo.ano_censo)') - .from('adh_analfabetismo'); - - const analfabQry = squel.select() - .field('regiao.nome', 'name') - .field('AVG(t_analf15m)', 'analfabetism') - .field('ano_censo', 'census_year') - .from('adh_analfabetismo') - .from('municipio') - .from('estado') - .from('regiao') - .where(`regiao.pk_regiao_id = ${regionId}`) - .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`) - .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .where('estado.fk_regiao_id = regiao.pk_regiao_id') - .group('regiao.nome') - .group('adh_analfabetismo.ano_censo') - .order('regiao.nome'); - - const giniYearQry = squel.select() - .field('MAX(adh_gini.ano_censo)') - .from('adh_gini'); - - const giniQry = squel.select() - .field('regiao.nome', 'name') - .field('AVG(gini)', 'gini') - .field('ano_censo', 'census_year') - .from('adh_gini') - .from('municipio') - .from('estado') - .from('regiao') - .where(`regiao.pk_regiao_id = ${regionId}`) - .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`) - .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .where('estado.fk_regiao_id = regiao.pk_regiao_id') - .group('regiao.nome') - .group('adh_gini.ano_censo') - .order('regiao.nome'); - // 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('location')); - -locationApp.get('/sociodemographic/state/:id', (req, res, next) => { - const stateId = parseInt(req.params.id, 10); - - const populationYearQry = squel.select() - .field('MAX(ibge_populacao.ano_censo)') - .from('ibge_populacao'); - // load all cities by state and compute the sociodemographic and educational information - const populationQry = squel.select() - .field('estado.nome', 'name') - .field('SUM(populacao)', 'population') - .field('ibge_populacao.ano_censo', 'census_year') - .from('ibge_populacao') - .from('municipio') - .from('estado') - .where(`estado.pk_estado_id = ${stateId}`) - .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`) - .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .group('estado.nome') - .group('ibge_populacao.ano_censo') - .order('estado.nome'); - - const pibYearQry = squel.select() - .field('MAX(ibge_pib.ano_censo)') - .from('ibge_pib'); - - const pibQry = squel.select() - .field('estado.nome', 'name') - .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita') - .field('ibge_pib.ano_censo', 'census_year') - .from('ibge_pib') - .from('municipio') - .from('estado') - .where(`estado.pk_estado_id = ${stateId}`) - .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`) - .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .group('estado.nome') - .group('ibge_pib.ano_censo') - .order('estado.nome'); - - const idhYearQry = squel.select() - .field('MAX(adh_idh.ano_censo)') - .from('adh_idh'); - - const idhQry = squel.select() - .field('estado.nome', 'name') - .field('AVG(idhm)', 'idhm') - .field('adh_idh.ano_censo', 'census_year') - .from('adh_idh') - .from('municipio') - .from('estado') - .where(`estado.pk_estado_id = ${stateId}`) - .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`) - .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .group('estado.nome') - .group('adh_idh.ano_censo') - .order('estado.nome'); - - const analfabYearQry = squel.select() - .field('MAX(adh_analfabetismo.ano_censo)') - .from('adh_analfabetismo'); - - const analfabQry = squel.select() - .field('estado.nome', 'name') - .field('AVG(t_analf15m)', 'analfabetism') - .field('adh_analfabetismo.ano_censo', 'census_year') - .from('adh_analfabetismo') - .from('municipio') - .from('estado') - .where(`estado.pk_estado_id = ${stateId}`) - .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`) - .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .group('estado.nome') - .group('adh_analfabetismo.ano_censo') - .order('estado.nome'); - - const giniYearQry = squel.select() - .field('MAX(adh_gini.ano_censo)') - .from('adh_gini'); - - const giniQry = squel.select() - .field('estado.nome', 'name') - .field('AVG(gini)', 'gini') - .field('adh_gini.ano_censo', 'census_year') - .from('adh_gini') - .from('municipio') - .from('estado') - .where(`estado.pk_estado_id = ${stateId}`) - .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`) - .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge') - .where('municipio.fk_estado_id = estado.pk_estado_id') - .group('estado.nome') - .group('adh_gini.ano_censo') - .order('estado.nome'); - // 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('location')); - -locationApp.get('/sociodemographic/city/:id', (req, res, next) => { - const cityId = parseInt(req.params.id, 10); - - const populationYearQry = squel.select() - .field('MAX(ibge_populacao.ano_censo)') - .from('ibge_populacao'); - // load all cities by state and compute the sociodemographic and educational information - const populationQry = squel.select() - .field('municipio.nome', 'name') - .field('SUM(populacao)', 'population') - .field('ibge_populacao.ano_censo', 'census_year') - .from('ibge_populacao') - .from('municipio') - .where(`municipio.pk_cod_ibge = ${cityId}`) - .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`) - .where('ibge_populacao.fk_municipio_id = municipio.pk_cod_ibge') - .group('municipio.nome') - .group('ibge_populacao.ano_censo') - .order('municipio.nome'); - - const pibYearQry = squel.select() - .field('MAX(ibge_pib.ano_censo)') - .from('ibge_pib'); - - const pibQry = squel.select() - .field('municipio.nome', 'name') - .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita') - .field('ibge_pib.ano_censo', 'census_year') - .from('ibge_pib') - .from('municipio') - .where(`municipio.pk_cod_ibge = ${cityId}`) - .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`) - .where('ibge_pib.fk_municipio_id = municipio.pk_cod_ibge') - .group('municipio.nome') - .group('ibge_pib.ano_censo') - .order('municipio.nome'); - - const idhYearQry = squel.select() - .field('MAX(adh_idh.ano_censo)') - .from('adh_idh'); - - const idhQry = squel.select() - .field('municipio.nome', 'name') - .field('AVG(idhm)', 'idhm') - .field('adh_idh.ano_censo', 'census_year') - .from('adh_idh') - .from('municipio') - .where(`municipio.pk_cod_ibge = ${cityId}`) - .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`) - .where('adh_idh.fk_municipio_id = municipio.pk_cod_ibge') - .group('municipio.nome') - .group('adh_idh.ano_censo') - .order('municipio.nome'); - - const analfabYearQry = squel.select() - .field('MAX(adh_analfabetismo.ano_censo)') - .from('adh_analfabetismo'); - - const analfabQry = squel.select() - .field('municipio.nome', 'name') - .field('AVG(t_analf15m)', 'analfabetism') - .field('adh_analfabetismo.ano_censo', 'census_year') - .from('adh_analfabetismo') - .from('municipio') - .where(`municipio.pk_cod_ibge = ${cityId}`) - .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`) - .where('adh_analfabetismo.fk_municipio_id = municipio.pk_cod_ibge') - .group('municipio.nome') - .group('adh_analfabetismo.ano_censo') - .order('municipio.nome'); - - const giniYearQry = squel.select() - .field('MAX(adh_gini.ano_censo)') - .from('adh_gini'); - - const giniQry = squel.select() - .field('municipio.nome', 'name') - .field('AVG(gini)', 'gini') - .field('adh_gini.ano_censo', 'census_year') - .from('adh_gini') - .from('municipio') - .where(`municipio.pk_cod_ibge = ${cityId}`) - .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`) - .where('adh_gini.fk_municipio_id = municipio.pk_cod_ibge') - .group('municipio.nome') - .group('adh_gini.ano_censo') - .order('municipio.nome'); - // 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('location')); - -locationApp.get('/educational', (req, res, next) => { - const censusYearQry = squel.select() - .field('MAX(escola.ano_censo)', 'ano_censo') - .from('escola') - .toString(); - - const totalSchoolsQry = squel.select() - .field('\'Brasil\'', 'name') - .field('0', 'location') - .field('COUNT(DISTINCT(escola.cod_entidade))', 'total') - .field('escola.ano_censo', 'census_year') - .from('escola') - .where(`escola.ano_censo IN (${censusYearQry})`) - .where('escola.id_tipo_turma = 0') - .group('escola.ano_censo'); - - const schoolsPerLocationQry = squel.select() - .field('\'Brasil\'', 'name') - .field('escola.tipo_localizacao', 'location') - .field('COUNT(DISTINCT(escola.cod_entidade))', 'total') - .field('escola.ano_censo', 'census_year') - .from('escola') - .where(`escola.ano_censo IN (${censusYearQry})`) - .where('escola.id_tipo_turma = 0') - .group('escola.tipo_localizacao') - .group('escola.ano_censo') - .order('escola.tipo_localizacao'); - - const schoolClassYearQry = squel.select() - .field('MAX(turma.ano_censo)') - .from('turma') - .toString(); - - const enrollmentsQry = squel.select() - .field('\'Brasil\'', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .from('turma') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .where('turma.fk_tipo_turma_id <= 3') - .group('turma.ano_censo'); - - const enrollmentsPerAdmDepQry = squel.select() - .field('\'Brasil\'', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('dependencia_adm.nome', 'adm_dependency') - .from('turma') - .from('dependencia_adm') - .where('turma.fk_dependencia_adm_id = dependencia_adm.pk_dependencia_adm_id') - .where('turma.fk_tipo_turma_id <= 3') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .group('turma.ano_censo') - .group('dependencia_adm.nome') - .order('dependencia_adm.nome'); - - const enrollmentsPerSchoolLevelQry = squel.select() - .field('\'Brasil\'', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('etapa_ensino.desc_etapa', 'school_level') - .from('turma') - .from('etapa_ensino') - .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id') - .where('turma.fk_tipo_turma_id <= 3') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .group('turma.ano_censo') - .group('etapa_ensino.desc_etapa') - .group('etapa_ensino.pk_etapa_ensino_id') - .order('etapa_ensino.pk_etapa_ensino_id'); - - const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep", - "enrollment_per_school_level" ]; - const querySet = [ totalSchoolsQry, schoolsPerLocationQry, enrollmentsQry, - enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry]; - // wait until all queries finish or one of them fail - Promise.all(dbExecAll(querySet)).then((queryResults) => { - req.result = processResultSet(queryResults, queryLabels); - for(let label in req.result) { - for(let row of req.result[label]) { - if (row.hasOwnProperty('location')) { - row.location = locationIdToStr(row.location); - } - } - } - next(); - }).catch((error) => { - log.error(`[SQL query error] ${error}`); - next(error); - }); -}, response('location')); - -locationApp.get('/educational/region/:id', (req, res, next) => { - const regionId = parseInt(req.params.id, 10); - - const censusYearQry = squel.select() - .field('MAX(escola.ano_censo)', 'ano_censo') - .from('escola') - .toString(); - - const totalSchoolsQry = squel.select() - .field('regiao.nome', 'name') - .field('0', 'location') - .field('COUNT(DISTINCT(escola.cod_entidade))', 'total') - .field('escola.ano_censo', 'census_year') - .from('escola') - .from('estado') - .from('regiao') - .where('escola.fk_estado_id = estado.pk_estado_id') - .where('estado.fk_regiao_id = regiao.pk_regiao_id') - .where(`regiao.pk_regiao_id = ${regionId}`) - .where(`escola.ano_censo IN (${censusYearQry})`) - .where('escola.id_tipo_turma = 0') - .group('regiao.nome') - .group('escola.ano_censo') - .order('regiao.nome'); - - const schoolsPerLocationQry = squel.select() - .field('regiao.nome', 'name') - .field('escola.tipo_localizacao', 'location') - .field('COUNT(DISTINCT(escola.cod_entidade))', 'total') - .field('escola.ano_censo', 'census_year') - .from('escola') - .from('estado') - .from('regiao') - .where('escola.fk_estado_id = estado.pk_estado_id') - .where('estado.fk_regiao_id = regiao.pk_regiao_id') - .where(`regiao.pk_regiao_id = ${regionId}`) - .where(`escola.ano_censo IN (${censusYearQry})`) - .where('escola.id_tipo_turma = 0') - .group('regiao.nome') - .group('escola.tipo_localizacao') - .group('escola.ano_censo') - .order('regiao.nome') - .order('escola.tipo_localizacao'); - - const schoolClassYearQry = squel.select() - .field('MAX(turma.ano_censo)') - .from('turma') - .toString(); - - const enrollmentsQry = squel.select() - .field('regiao.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .from('turma') - .from('regiao') - .where('turma.fk_regiao_id = regiao.pk_regiao_id') - .where(`turma.fk_regiao_id = ${regionId}`) - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .where('turma.fk_tipo_turma_id <= 3') - .group('turma.ano_censo') - .group('regiao.nome') - .order('regiao.nome'); - - const enrollmentsPerAdmDepQry = squel.select() - .field('regiao.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('dependencia_adm.nome', 'adm_dependency') - .from('turma') - .from('dependencia_adm') - .from('regiao') - .where('turma.fk_regiao_id = regiao.pk_regiao_id') - .where(`turma.fk_regiao_id = ${regionId}`) - .where('turma.fk_dependencia_adm_id = dependencia_adm.pk_dependencia_adm_id') - .where('turma.fk_tipo_turma_id <= 3') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .group('turma.ano_censo') - .group('dependencia_adm.nome') - .group('regiao.nome') - .order('regiao.nome') - .order('dependencia_adm.nome'); - - const enrollmentsPerSchoolLevelQry = squel.select() - .field('regiao.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('etapa_ensino.desc_etapa', 'school_level') - .from('turma') - .from('etapa_ensino') - .from('regiao') - .where('turma.fk_regiao_id = regiao.pk_regiao_id') - .where(`turma.fk_regiao_id = ${regionId}`) - .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id') - .where('turma.fk_tipo_turma_id <= 3') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .group('turma.ano_censo') - .group('etapa_ensino.desc_etapa') - .group('regiao.nome') - .group('etapa_ensino.pk_etapa_ensino_id') - .order('regiao.nome') - .order('etapa_ensino.pk_etapa_ensino_id'); - - const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep", - "enrollment_per_school_level" ]; - const querySet = [ totalSchoolsQry, schoolsPerLocationQry, enrollmentsQry, - enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry]; - // wait until all queries finish or one of them fail - Promise.all(dbExecAll(querySet)).then((queryResults) => { - req.result = processResultSet(queryResults, queryLabels); - for(let label in req.result) { - for(let row of req.result[label]) { - if (row.hasOwnProperty('location')) { - row.location = locationIdToStr(row.location); - } - } - } - next(); - }).catch((error) => { - log.error(`[SQL query error] ${error}`); - next(error); - }); -}, response('location')); - -locationApp.get('/educational/state/:id', (req, res, next) => { - const stateId = parseInt(req.params.id, 10); - - const censusYearQry = squel.select() - .field('MAX(escola.ano_censo)', 'ano_censo') - .from('escola') - .toString(); - - const totalSchoolsQry = squel.select() - .field('estado.nome', 'name') - .field('0', 'location') - .field('COUNT(DISTINCT(escola.cod_entidade))', 'total') - .field('escola.ano_censo', 'census_year') - .from('escola') - .from('estado') - .where('escola.fk_estado_id = estado.pk_estado_id') - .where(`escola.fk_estado_id = ${stateId}`) - .where(`escola.ano_censo IN (${censusYearQry})`) - .where('escola.id_tipo_turma = 0') - .group('estado.nome') - .group('escola.ano_censo') - .order('estado.nome'); - - const schoolsPerLocationQry = squel.select() - .field('estado.nome', 'name') - .field('escola.tipo_localizacao', 'location') - .field('COUNT(DISTINCT(escola.cod_entidade))', 'total') - .field('escola.ano_censo', 'census_year') - .from('escola') - .from('estado') - .where('escola.fk_estado_id = estado.pk_estado_id') - .where(`escola.fk_estado_id = ${stateId}`) - .where(`escola.ano_censo IN (${censusYearQry})`) - .where('escola.id_tipo_turma = 0') - .group('estado.nome') - .group('escola.tipo_localizacao') - .group('escola.ano_censo') - .order('estado.nome') - .order('escola.tipo_localizacao'); - - const schoolClassYearQry = squel.select() - .field('MAX(turma.ano_censo)') - .from('turma') - .toString(); - - const enrollmentsQry = squel.select() - .field('estado.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .from('turma') - .from('estado') - .where('turma.fk_estado_id = estado.pk_estado_id') - .where(`turma.fk_estado_id = ${stateId}`) - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .where('turma.fk_tipo_turma_id <= 3') - .group('turma.ano_censo') - .group('estado.nome') - .order('estado.nome'); - - const enrollmentsPerAdmDepQry = squel.select() - .field('estado.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('dependencia_adm.nome', 'adm_dependency') - .from('turma') - .from('dependencia_adm') - .from('estado') - .where('turma.fk_estado_id = estado.pk_estado_id') - .where(`turma.fk_estado_id = ${stateId}`) - .where('turma.fk_dependencia_adm_id = dependencia_adm.pk_dependencia_adm_id') - .where('turma.fk_tipo_turma_id <= 3') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .group('turma.ano_censo') - .group('dependencia_adm.nome') - .group('estado.nome') - .order('estado.nome') - .order('dependencia_adm.nome'); - - const enrollmentsPerSchoolLevelQry = squel.select() - .field('estado.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('etapa_ensino.desc_etapa', 'school_level') - .from('turma') - .from('etapa_ensino') - .from('estado') - .where('turma.fk_estado_id = estado.pk_estado_id') - .where(`turma.fk_estado_id = ${stateId}`) - .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id') - .where('turma.fk_tipo_turma_id <= 3') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .group('turma.ano_censo') - .group('etapa_ensino.desc_etapa') - .group('etapa_ensino.pk_etapa_ensino_id') - .group('estado.nome') - .order('estado.nome') - .order('etapa_ensino.pk_etapa_ensino_id'); - - const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep", - "enrollment_per_school_level" ]; - const querySet = [ totalSchoolsQry, schoolsPerLocationQry, enrollmentsQry, - enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry]; - // wait until all queries finish or one of them fail - Promise.all(dbExecAll(querySet)).then((queryResults) => { - req.result = processResultSet(queryResults, queryLabels); - for(let label in req.result) { - for(let row of req.result[label]) { - if (row.hasOwnProperty('location')) { - row.location = locationIdToStr(row.location); - } - } - } - next(); - }).catch((error) => { - log.error(`[SQL query error] ${error}`); - next(error); - }); -}, response('location')); - -locationApp.get('/educational/city/:id', (req, res, next) => { - const cityId = parseInt(req.params.id, 10); - - const censusYearQry = squel.select() - .field('MAX(escola.ano_censo)', 'ano_censo') - .from('escola') - .toString(); - - const totalSchoolsQry = squel.select() - .field('municipio.nome', 'name') - .field('0', 'location') - .field('COUNT(DISTINCT(escola.cod_entidade))', 'total') - .field('escola.ano_censo', 'census_year') - .from('escola') - .from('municipio') - .where('escola.fk_municipio_id = municipio.pk_cod_ibge') - .where(`escola.fk_municipio_id = ${cityId}`) - .where(`escola.ano_censo IN (${censusYearQry})`) - .where('escola.id_tipo_turma = 0') - .group('municipio.nome') - .group('escola.ano_censo') - .order('municipio.nome'); - - const schoolsPerLocationQry = squel.select() - .field('municipio.nome', 'name') - .field('escola.tipo_localizacao', 'location') - .field('COUNT(DISTINCT(escola.cod_entidade))', 'total') - .field('escola.ano_censo', 'census_year') - .from('escola') - .from('municipio') - .where('escola.fk_municipio_id = municipio.pk_cod_ibge') - .where(`escola.fk_municipio_id = ${cityId}`) - .where(`escola.ano_censo IN (${censusYearQry})`) - .where('escola.id_tipo_turma = 0') - .group('municipio.nome') - .group('escola.tipo_localizacao') - .group('escola.ano_censo') - .order('municipio.nome') - .order('escola.tipo_localizacao'); - - const schoolClassYearQry = squel.select() - .field('MAX(turma.ano_censo)') - .from('turma') - .toString(); - - const enrollmentsQry = squel.select() - .field('municipio.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .from('turma') - .from('municipio') - .where('turma.fk_municipio_id = municipio.pk_cod_ibge') - .where(`turma.fk_municipio_id = ${cityId}`) - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .where('turma.fk_tipo_turma_id <= 3') - .group('turma.ano_censo') - .group('municipio.nome') - .order('municipio.nome'); - - const enrollmentsPerAdmDepQry = squel.select() - .field('municipio.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('dependencia_adm.nome', 'adm_dependency') - .from('turma') - .from('dependencia_adm') - .from('municipio') - .where('turma.fk_municipio_id = municipio.pk_cod_ibge') - .where(`turma.fk_municipio_id = ${cityId}`) - .where('turma.fk_dependencia_adm_id = dependencia_adm.pk_dependencia_adm_id') - .where('turma.fk_tipo_turma_id <= 3') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .group('turma.ano_censo') - .group('dependencia_adm.nome') - .group('municipio.nome') - .order('municipio.nome') - .order('dependencia_adm.nome'); - - const enrollmentsPerSchoolLevelQry = squel.select() - .field('municipio.nome', 'name') - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('etapa_ensino.desc_etapa', 'school_level') - .from('turma') - .from('etapa_ensino') - .from('municipio') - .where('turma.fk_municipio_id = municipio.pk_cod_ibge') - .where(`turma.fk_municipio_id = ${cityId}`) - .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id') - .where('turma.fk_tipo_turma_id <= 3') - .where(`turma.ano_censo IN (${schoolClassYearQry})`) - .group('turma.ano_censo') - .group('etapa_ensino.desc_etapa') - .group('etapa_ensino.pk_etapa_ensino_id') - .group('municipio.nome') - .order('municipio.nome') - .order('etapa_ensino.pk_etapa_ensino_id'); - - const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep", - "enrollment_per_school_level" ]; - const querySet = [ totalSchoolsQry, schoolsPerLocationQry, enrollmentsQry, - enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry]; - // wait until all queries finish or one of them fail - Promise.all(dbExecAll(querySet)).then((queryResults) => { - req.result = processResultSet(queryResults, queryLabels); - for(let label in req.result) { - for(let row of req.result[label]) { - if (row.hasOwnProperty('location')) { - row.location = locationIdToStr(row.location); - } - } - } - next(); - }).catch((error) => { - log.error(`[SQL query error] ${error}`); - next(error); - }); -}, response('location')); - -locationApp.get('/educational/school_level', (req, res, next) => { - const enrollmentsPerSchoolLevelYearQry = squel.select() - .field('MAX(turma.ano_censo)', 'census_year') - .from('turma'); - - const enrollmentsPerSchoolLevelQry = squel.select() - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('turma.serie_ano', 'school_year') - .field('etapa_ensino.desc_etapa', 'school_level') - .from('turma') - .from('etapa_ensino') - .where(`turma.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`) - .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id') - .where('turma.fk_tipo_turma_id <= 3') - .group('etapa_ensino.desc_etapa') - .group('etapa_ensino.pk_etapa_ensino_id') - .group('turma.serie_ano') - .group('turma.ano_censo') - .order('etapa_ensino.pk_etapa_ensino_id') - .order('turma.serie_ano') - .order('turma.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 = schoolYearIdToStr(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('location')); - -locationApp.get('/educational/school_level/region/:id', (req, res, next) => { - const regionId = parseInt(req.params.id, 10); - - const enrollmentsPerSchoolLevelYearQry = squel.select() - .field('MAX(turma.ano_censo)', 'census_year') - .from('turma'); - - const enrollmentsPerSchoolLevelQry = squel.select() - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('turma.serie_ano', 'school_year') - .field('etapa_ensino.desc_etapa', 'school_level') - .from('turma') - .from('etapa_ensino') - .from('regiao') - .where(`turma.fk_regiao_id = ${regionId}`) - .where('turma.fk_regiao_id = regiao.pk_regiao_id') - .where(`turma.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`) - .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id') - .where('turma.fk_tipo_turma_id <= 3') - .group('regiao.nome') - .group('etapa_ensino.desc_etapa') - .group('etapa_ensino.pk_etapa_ensino_id') - .group('turma.serie_ano') - .group('turma.ano_censo') - .order('regiao.nome') - .order('etapa_ensino.pk_etapa_ensino_id') - .order('turma.serie_ano') - .order('turma.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 = schoolYearIdToStr(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('location')); - -locationApp.get('/educational/school_level/state/:id', (req, res, next) => { - const stateId = parseInt(req.params.id, 10); - - const enrollmentsPerSchoolLevelYearQry = squel.select() - .field('MAX(turma.ano_censo)', 'census_year') - .from('turma'); - - const enrollmentsPerSchoolLevelQry = squel.select() - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('turma.serie_ano', 'school_year') - .field('etapa_ensino.desc_etapa', 'school_level') - .from('turma') - .from('etapa_ensino') - .from('estado') - .where(`turma.fk_estado_id = ${stateId}`) - .where('turma.fk_estado_id = estado.pk_estado_id') - .where(`turma.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`) - .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id') - .where('turma.fk_tipo_turma_id <= 3') - .group('estado.nome') - .group('etapa_ensino.desc_etapa') - .group('etapa_ensino.pk_etapa_ensino_id') - .group('turma.serie_ano') - .group('turma.ano_censo') - .order('estado.nome') - .order('etapa_ensino.pk_etapa_ensino_id') - .order('turma.serie_ano') - .order('turma.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 = schoolYearIdToStr(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('location')); - -locationApp.get('/educational/school_level/city/:id', (req, res, next) => { - const cityId = parseInt(req.params.id, 10); - - const enrollmentsPerSchoolLevelYearQry = squel.select() - .field('MAX(turma.ano_censo)', 'census_year') - .from('turma'); - - const enrollmentsPerSchoolLevelQry = squel.select() - .field('COALESCE(SUM(turma.num_matriculas), 0)', 'total') - .field('turma.ano_censo', 'census_year') - .field('turma.serie_ano', 'school_year') - .field('etapa_ensino.desc_etapa', 'school_level') - .from('turma') - .from('etapa_ensino') - .from('municipio') - .where(`turma.fk_municipio_id = ${cityId}`) - .where('turma.fk_municipio_id = municipio.pk_cod_ibge') - .where(`turma.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`) - .where('turma.fk_etapa_ensino_id = etapa_ensino.pk_etapa_ensino_id') - .where('turma.fk_tipo_turma_id <= 3') - .group('municipio.nome') - .group('etapa_ensino.desc_etapa') - .group('etapa_ensino.pk_etapa_ensino_id') - .group('turma.serie_ano') - .group('turma.ano_censo') - .order('municipio.nome') - .order('etapa_ensino.pk_etapa_ensino_id') - .order('turma.serie_ano') - .order('turma.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 = schoolYearIdToStr(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('location')); - -module.exports = locationApp; diff --git a/src/libs/routes/region.js b/src/libs/routes/region.js index f152d899c05b99334725f52d17b71199b4a2f4dc..a752fa1b6169522d1f2f0983f3c4b7d94748f551 100644 --- a/src/libs/routes/region.js +++ b/src/libs/routes/region.js @@ -21,11 +21,11 @@ rqf.addField({ }).addValue({ name: 'id', table: 'regiao', - tableField: 'pk_regiao_id', + tableField: 'id', where: { relation: '=', type: 'integer', - field: 'pk_regiao_id', + field: 'id', table: 'regiao' } }).addField({ @@ -46,7 +46,7 @@ rqf.addField({ regionApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { req.sql.from('regiao') - .field('pk_regiao_id', 'id') + .field('id') .field('nome', 'name'); next(); }, query, response('region')); diff --git a/src/libs/routes/school.js b/src/libs/routes/school.js index 6420db5d6b1fd64940e82eefa3e624b065ba3c90..d6e5f7d013f5b0bf11f14a3456273bf64ff0cfca 100644 --- a/src/libs/routes/school.js +++ b/src/libs/routes/school.js @@ -21,11 +21,11 @@ rqf.addField({ }).addValue({ name: 'id', table: 'escola', - tableField: 'cod_entidade', + tableField: 'id', where: { relation: '=', type: 'integer', - field: 'cod_entidade' + field: 'id' } }).addValue({ name: 'city', @@ -35,12 +35,12 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'fk_municipio_id', + field: 'municipio_id', table: 'escola' }, join: { - primary: 'pk_cod_ibge', - foreign: 'fk_municipio_id', + primary: 'id', + foreign: 'municipio_id', foreignTable: 'escola' } }).addValue({ @@ -51,14 +51,25 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'fk_estado_id', + field: 'estado_id', table: 'escola' }, join: { - primary: 'pk_estado_id', - foreign: 'fk_estado_id', + 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' + } }); schoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { @@ -71,10 +82,11 @@ schoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { }); } req.sql.from('escola') - .field('escola.cod_entidade', 'id') + .field('escola.id') .field('escola.ano_censo', 'year') - .field('escola.fk_estado_id', 'state_id') - .field('escola.fk_municipio_id', 'city_id'); + .field('escola.nome_escola', 'name') + .field('escola.estado_id', 'state_id') + .field('escola.municipio_id', 'city_id'); next(); }, query, response('school')); diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js new file mode 100644 index 0000000000000000000000000000000000000000..2196c7a2d961ea54591abb63d808c7268c02394f --- /dev/null +++ b/src/libs/routes/spatial.js @@ -0,0 +1,459 @@ +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`); + +const sqlQuery = require(`${libs}/db/query_exec`); + +const response = require(`${libs}/middlewares/response`); + +const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`); + +const spatialApp = express(); + +let rqf = new ReqQueryFields(); + +function schoolYearIdToStr(schoolYearId) { + let schoolYearStr; + switch(schoolYearId) { + case 11: + schoolYearStr = 'Creche'; + break; + case 21: + schoolYearStr = 'Pré-escola'; + break; + case 31: + schoolYearStr = '1 Ano'; + break; + case 32: + schoolYearStr = '2 Ano - 1 Serie'; + break; + case 33: + schoolYearStr = '3 Ano - 2 Serie'; + break; + case 34: + schoolYearStr = '4 Ano - 3 Serie'; + break; + case 35: + schoolYearStr = '5 Ano - 4 Serie'; + break; + case 41: + schoolYearStr = '6 Ano - 5 Serie'; + break; + case 42: + schoolYearStr = '7 Ano - 6 Serie'; + break; + case 43: + schoolYearStr = '8 Ano - 7 Serie'; + break; + case 44: + schoolYearStr = '9 Ano - 8 Serie'; + break; + case 51: + schoolYearStr = '1 Ano'; // equivalent to 'EM 1 Série' + break; + case 52: + schoolYearStr = '2 Ano'; // equivalent to 'EM 2 Série' + break; + case 53: + schoolYearStr = '3 Ano'; // equivalent to 'EM 3 Série' + break; + case 54: + schoolYearStr = '4 Ano'; // equivalent to 'EM 4 Série' + break; + case 61: + schoolYearStr = 'EJA AI'; + break; + case 62: + schoolYearStr = 'EJA AF'; + break; + case 63: + schoolYearStr = 'EJA EM'; + break; + case 64: + schoolYearStr = 'EJA semi-presencial'; + break; + case 71: + schoolYearStr = 'EP'; + break; + case 81: + schoolYearStr = 'Atividades complementares e AEE'; + break; + default: + schoolYearStr = 'Não classificado'; + } + return schoolYearStr; +} + +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) { + log.debug(row); + resultMap[resultLbl].push(row); + } + } + resultIdx++; + } + log.debug(resultMap); + 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 schoolClassYearQry = squel.select() + .field('MAX(matricula.ano_censo)') + .from('matricula') + .toString(); + + const enrollmentsQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(COUNT(matricula.id), 0)', 'total') + .field('matricula.ano_censo', 'census_year') + .from('matricula') + .where(`matricula.ano_censo IN (${schoolClassYearQry})`) + .where('matricula.tipo<=3') + .group('matricula.ano_censo'); + + const enrollmentsPerAdmDepQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(COUNT(matricula.id), 0)', 'total') + .field('matricula.ano_censo', 'census_year') + .field('dependencia_adm.nome', 'adm_dependency_name') + .from('dependencia_adm') + .from('matricula') + .where('matricula.dependencia_adm_id=dependencia_adm.id') + .where('matricula.tipo <= 3') + .where(`matricula.ano_censo IN (${schoolClassYearQry})`) + .group('matricula.ano_censo') + .group('dependencia_adm.nome') + .order('dependencia_adm.nome'); + + const enrollmentsPerSchoolLevelQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(COUNT(matricula.id), 0)', 'total') + .field('matricula.ano_censo', 'census_year') + .field('etapa_ensino.desc_etapa', 'school_level_name') + .from('etapa_ensino') + .from('matricula') + .where('matricula.etapa_ensino_id=etapa_ensino.id') + .where('matricula.tipo <= 3') + .where(`matricula.ano_censo IN (${schoolClassYearQry})`) + .group('matricula.ano_censo') + .group('etapa_ensino.desc_etapa') + .group('etapa_ensino.id') + .order('etapa_ensino.id'); + + const enrollmentsPerLocationQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(COUNT(matricula.id), 0)', 'total') + .field('matricula.ano_censo', 'census_year') + .field('localizacao.descricao', 'location_name') + .from('localizacao') + .from('matricula') + .where('matricula.localizacao_id=localizacao.id') + .where('matricula.tipo <= 3') + .where(`matricula.ano_censo IN (${schoolClassYearQry})`) + .group('matricula.ano_censo') + .group('localizacao.descricao') + .order('ano_censo') + .order('localizacao.descricao'); + + const enrollmentsPerSchoolLevelAndAdmDependencyQry = req.sql.clone() + .field('\'Brasil\'', 'name') + .field('COALESCE(COUNT(matricula.id), 0)', 'total') + .field('matricula.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('matricula') + .where('matricula.etapa_ensino_id=etapa_ensino.id') + .where('matricula.dependencia_adm_id=dependencia_adm.id') + .where('matricula.tipo <= 3') + .where(`matricula.ano_censo IN (${schoolClassYearQry})`) + .group('matricula.ano_censo') + .group('etapa_ensino.desc_etapa') + .group('dependencia_adm.nome') + .order('matricula.ano_censo') + .order('etapa_ensino.desc_etapa') + .order('dependencia_adm.nome'); + + const queryLabels = [ "school", "school_per_location", "school_per_adm_dependency", "enrollment", "enrollment_per_adm_dep", + "enrollment_per_school_level", "enrollment_per_location", "enrollment_per_school_level_adm_dependency" ]; + const querySet = [ totalSchoolsQry, schoolsPerLocationQry, schoolsPerAdmDependencyQry, enrollmentsQry, + enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry, enrollmentsPerLocationQry, enrollmentsPerSchoolLevelAndAdmDependencyQry]; + // 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 = schoolYearIdToStr(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 4da632605c9fd580937a5569450abfaad3b16e89..2663fe9890849aea38ae51244e330454983a3742 100644 --- a/src/libs/routes/state.js +++ b/src/libs/routes/state.js @@ -21,11 +21,11 @@ rqf.addField({ }).addValue({ name: 'id', table: 'estado', - tableField: 'pk_estado_id', + tableField: 'id', where: { relation: '=', type: 'integer', - field: 'pk_estado_id' + field: 'id' } }).addValue({ name: 'region', @@ -35,12 +35,12 @@ rqf.addField({ where: { relation: '=', type: 'integer', - field: 'fk_regiao_id', + field: 'regiao_id', table: 'estado' }, join: { - primary: 'pk_regiao_id', - foreign: 'fk_regiao_id', + primary: 'id', + foreign: 'regiao_id', foreignTable: 'estado' } }).addField({ @@ -60,10 +60,10 @@ rqf.addField({ stateApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { req.sql.from('estado') - .field('pk_estado_id', 'id') - .group('pk_estado_id') - .field('fk_regiao_id', 'region_id') - .group('fk_regiao_id') + .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') diff --git a/src/test/enrollment.js b/src/test/enrollment.js index b40c2de866873f131bd35bc85eb44ee2a9597eee..48bebc5f0beaa7883e618f3a9d64004f023f9e1c 100644 --- a/src/test/enrollment.js +++ b/src/test/enrollment.js @@ -68,7 +68,63 @@ describe('request enrollments', () => { it('should list the administrative dependencies', (done) => { chai.request(server) - .get('/api/v1/enrollment/adm_dependency ') + .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 the administrative dependencies detailed', (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 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; @@ -155,7 +211,7 @@ describe('request enrollments', () => { it('should list enrollments with valid dimensions and filters', (done) => { chai.request(server) - .get('/api/v1/enrollment?dims=region,state,education_level,school&filter=min_year:2013,max_year:2014,city:4106902') + .get('/api/v1/enrollment?dims=region,state,education_level,school,gender,period&filter=min_year:2015,max_year:2015,city:4106902') .end((err, res) => { res.should.have.status(200); res.should.be.json; @@ -163,8 +219,8 @@ describe('request enrollments', () => { 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('school_name'); + res.body.result[0].should.have.property('education_level_name'); res.body.result[0].should.have.property('total'); res.body.result[0].should.have.property('year'); done(); diff --git a/src/test/query.js b/src/test/query.js index 6d3454452d882fb34f3d75092e63d74cb3d3642d..ed492e8a3c30aa7de2bae19dab1bc23398a43bfd 100644 --- a/src/test/query.js +++ b/src/test/query.js @@ -77,7 +77,7 @@ describe('Query middleware', () => { it('should return 404 with an empty query result', (done) => { let req = { - sql: squel.select().field('*').from('regiao').where('pk_regiao_id>6') + sql: squel.select().field('*').from('regiao').where('id>6') }; let res = {}; query(req, {}, (error)=>{ diff --git a/src/test/location.js b/src/test/spatial.js similarity index 93% rename from src/test/location.js rename to src/test/spatial.js index 42761a8dadf84c63b1c4202fe27e17b9fa2aa920..b4422e7911a30b19e2d0f7a73a9b1d62389df1b4 100644 --- a/src/test/location.js +++ b/src/test/spatial.js @@ -24,12 +24,12 @@ const server = require(`${libs}/app`); chai.use(chaiHttp); -const testTimeout = 5000; +const testTimeout = 60000; -describe('test location', () => { +describe('test spatial', () => { it('should return the expected response format for sociodemographic data for the whole country', (done) => { chai.request(server) - .get('/api/v1/location/sociodemographic') + .get('/api/v1/spatial/sociodemographic') .end((err, res) => { res.should.have.status(200); // test response format @@ -69,7 +69,7 @@ describe('test location', () => { it('should return the expected response format for sociodemographic data for a region', (done) => { chai.request(server) - .get('/api/v1/location/sociodemographic/region/1') + .get('/api/v1/spatial/sociodemographic?filter=region:1') .end((err, res) => { res.should.have.status(200); // test response format @@ -109,7 +109,7 @@ describe('test location', () => { it('should return the expected response format for sociodemographic data for a state', (done) => { chai.request(server) - .get('/api/v1/location/sociodemographic/state/42') + .get('/api/v1/spatial/sociodemographic?filter=state:42') .end((err, res) => { res.should.have.status(200); // test response format @@ -149,7 +149,7 @@ describe('test location', () => { it('should return the expected response format for sociodemographic data for a city', (done) => { chai.request(server) - .get('/api/v1/location/sociodemographic/city/4106902') + .get('/api/v1/spatial/sociodemographic?filter=city:4106902') .end((err, res) => { res.should.have.status(200); // test response format @@ -189,7 +189,7 @@ describe('test location', () => { it('should return the expected response format for educational data for the whole country', (done) => { chai.request(server) - .get('/api/v1/location/educational') + .get('/api/v1/spatial/educational') .end((err, res) => { res.should.have.status(200); // test response format @@ -209,11 +209,10 @@ describe('test location', () => { res.body.result.should.have.property('enrollment_per_school_level'); res.body.result.enrollment_per_school_level.should.be.a('array'); // test response attributes for school - res.body.result.school.should.a('array'); res.body.result.school.forEach((row) => { row.should.be.a('object'); row.should.have.property('name'); - row.should.have.property('location'); + row.should.have.property('location_name'); row.should.have.property('total'); row.should.have.property('census_year'); }); @@ -221,7 +220,7 @@ describe('test location', () => { res.body.result.school_per_location.forEach((row) => { row.should.be.a('object'); row.should.have.property('name'); - row.should.have.property('location'); + row.should.have.property('location_name'); row.should.have.property('total'); row.should.have.property('census_year'); }); @@ -238,7 +237,7 @@ describe('test location', () => { row.should.have.property('name'); row.should.have.property('total'); row.should.have.property('census_year'); - row.should.have.property('adm_dependency'); + row.should.have.property('adm_dependency_name'); }); // test response attributes for enrollment_per_school_level res.body.result.enrollment_per_school_level.forEach((row) => { @@ -246,7 +245,7 @@ describe('test location', () => { row.should.have.property('name'); row.should.have.property('total'); row.should.have.property('census_year'); - row.should.have.property('school_level'); + row.should.have.property('school_level_name'); }); done(); }); @@ -254,7 +253,7 @@ describe('test location', () => { it('should return the expected response format for educational data for a country region', (done) => { chai.request(server) - .get('/api/v1/location/educational/region/1') + .get('/api/v1/spatial/educational?filter=region:1') .end((err, res) => { res.should.have.status(200); // test response format @@ -278,7 +277,7 @@ describe('test location', () => { res.body.result.school.forEach((row) => { row.should.be.a('object'); row.should.have.property('name'); - row.should.have.property('location'); + row.should.have.property('location_name'); row.should.have.property('total'); row.should.have.property('census_year'); }); @@ -286,7 +285,7 @@ describe('test location', () => { res.body.result.school_per_location.forEach((row) => { row.should.be.a('object'); row.should.have.property('name'); - row.should.have.property('location'); + row.should.have.property('location_name'); row.should.have.property('total'); row.should.have.property('census_year'); }); @@ -303,7 +302,7 @@ describe('test location', () => { row.should.have.property('name'); row.should.have.property('total'); row.should.have.property('census_year'); - row.should.have.property('adm_dependency'); + row.should.have.property('adm_dependency_name'); }); // test response attributes for enrollment_per_school_level res.body.result.enrollment_per_school_level.forEach((row) => { @@ -311,7 +310,7 @@ describe('test location', () => { row.should.have.property('name'); row.should.have.property('total'); row.should.have.property('census_year'); - row.should.have.property('school_level'); + row.should.have.property('school_level_name'); }); done(); }); @@ -319,7 +318,7 @@ describe('test location', () => { it('should return the expected response format for educational data for a country state', (done) => { chai.request(server) - .get('/api/v1/location/educational/state/42') + .get('/api/v1/spatial/educational?filter=state:42') .end((err, res) => { res.should.have.status(200); // test response format @@ -343,7 +342,7 @@ describe('test location', () => { res.body.result.school.forEach((row) => { row.should.be.a('object'); row.should.have.property('name'); - row.should.have.property('location'); + row.should.have.property('location_name'); row.should.have.property('total'); row.should.have.property('census_year'); }); @@ -351,7 +350,7 @@ describe('test location', () => { res.body.result.school_per_location.forEach((row) => { row.should.be.a('object'); row.should.have.property('name'); - row.should.have.property('location'); + row.should.have.property('location_name'); row.should.have.property('total'); row.should.have.property('census_year'); }); @@ -368,7 +367,7 @@ describe('test location', () => { row.should.have.property('name'); row.should.have.property('total'); row.should.have.property('census_year'); - row.should.have.property('adm_dependency'); + row.should.have.property('adm_dependency_name'); }); // test response attributes for enrollment_per_school_level res.body.result.enrollment_per_school_level.forEach((row) => { @@ -376,7 +375,7 @@ describe('test location', () => { row.should.have.property('name'); row.should.have.property('total'); row.should.have.property('census_year'); - row.should.have.property('school_level'); + row.should.have.property('school_level_name'); }); done(); }); @@ -384,7 +383,7 @@ describe('test location', () => { it('should return the expected response format for educational data for a country city', (done) => { chai.request(server) - .get('/api/v1/location/educational/city/4106902') + .get('/api/v1/spatial/educational?filter=city:4106902') .end((err, res) => { res.should.have.status(200); // test response format @@ -408,7 +407,7 @@ describe('test location', () => { res.body.result.school.forEach((row) => { row.should.be.a('object'); row.should.have.property('name'); - row.should.have.property('location'); + row.should.have.property('location_name'); row.should.have.property('total'); row.should.have.property('census_year'); }); @@ -416,7 +415,7 @@ describe('test location', () => { res.body.result.school_per_location.forEach((row) => { row.should.be.a('object'); row.should.have.property('name'); - row.should.have.property('location'); + row.should.have.property('location_name'); row.should.have.property('total'); row.should.have.property('census_year'); }); @@ -433,7 +432,7 @@ describe('test location', () => { row.should.have.property('name'); row.should.have.property('total'); row.should.have.property('census_year'); - row.should.have.property('adm_dependency'); + row.should.have.property('adm_dependency_name'); }); // test response attributes for enrollment_per_school_level res.body.result.enrollment_per_school_level.forEach((row) => { @@ -441,7 +440,7 @@ describe('test location', () => { row.should.have.property('name'); row.should.have.property('total'); row.should.have.property('census_year'); - row.should.have.property('school_level'); + row.should.have.property('school_level_name'); }); done(); }); @@ -449,7 +448,7 @@ describe('test location', () => { it('should return the correct format of enrollments per school level', (done) => { chai.request(server) - .get('/api/v1/location/educational/school_level') + .get('/api/v1/spatial/educational/school_level') .end((err, res) => { res.should.have.status(200); // test response format @@ -478,7 +477,7 @@ describe('test location', () => { it('should return the correct format of enrollments per school level for a region', (done) => { chai.request(server) - .get('/api/v1/location/educational/school_level/region/1') + .get('/api/v1/spatial/educational/school_level?filter=region:1') .end((err, res) => { res.should.have.status(200); // test response format @@ -507,7 +506,7 @@ describe('test location', () => { it('should return the correct format of enrollments per school level for a state', (done) => { chai.request(server) - .get('/api/v1/location/educational/school_level/state/42') + .get('/api/v1/spatial/educational/school_level?filter=state:42') .end((err, res) => { res.should.have.status(200); // test response format @@ -536,7 +535,7 @@ describe('test location', () => { it('should return the correct format of enrollments per school level for a city', (done) => { chai.request(server) - .get('/api/v1/location/educational/school_level/state/4106902') + .get('/api/v1/spatial/educational/school_level?filter=city:4106902') .end((err, res) => { res.should.have.status(200); // test response format