diff --git a/CHANGELOG.md b/CHANGELOG.md index e03ae02df25b3f55bfcd3a077e7c0af341eac5a5..794e49b1e118abfaa6a8fe16af4af04a8597c8df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,17 +4,25 @@ 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.6.0 - 2018-08-14 +## 1.6.0 - 2018-08-17 ### Added -- Add school infrastructure route -- Add new filters in enrollment route -- Add portal mec inep route +- Dimension state in route `enrollment` now returns state name and id +- `school` route now has a `search` parameter. You can search by city name or state abbreviation +- Added all age range convertion +- Added special class and all age range to id2str +- Added `school infratructure` route +- Added `INEP` route for Portal MEC ### Changed -- Fix auxiliar indicator +- Fix `auxiliar` indicator +- Add property "don't group" in RQF to dismiss the group by and order by clause +- Change RQF filters in `auxiliar` route +- Fix total classrooms needed bug in classroom count +- Add special class and all age range to enrollment route ## 1.5.0 - 2018-07-30 ### Added - Add portaMec route +- Fix bug in diagnosis and projection routes ## 1.4.2 - 2018-07-23 ### Added diff --git a/src/libs/convert/ageRangeAll.js b/src/libs/convert/ageRangeAll.js new file mode 100644 index 0000000000000000000000000000000000000000..2b4cab9cbf786a1a568912d9b3f7085e2789d26a --- /dev/null +++ b/src/libs/convert/ageRangeAll.js @@ -0,0 +1,28 @@ +module.exports = function ageRange(id) { + switch (id) { + case 1: + return '0 a 3 anos'; + case 2: + return '4 a 5 anos'; + case 3: + return '6 a 10 anos'; + case 4: + return '11 a 14 anos'; + case 5: + return '15 a 17 anos'; + case 6: + return '18 a 24 anos'; + case 7: + return '25 a 29 anos'; + case 8: + return '30 a 40 anos'; + case 9: + return '41 a 50 anos'; + case 10: + return '51 a 64 anos'; + case 11: + return 'Mais que 64 anos'; + default: + return 'Não declarada'; + } +}; diff --git a/src/libs/middlewares/id2str.js b/src/libs/middlewares/id2str.js index 0c71b4ccb22a5fd4bac37b20b314304c74c034f3..0a0c2af993a392d86b185e234c2eae448992d1b9 100644 --- a/src/libs/middlewares/id2str.js +++ b/src/libs/middlewares/id2str.js @@ -31,7 +31,6 @@ const useTransportPublic = require(`${libs}/convert/booleanVariable`); const transportationManager = require(`${libs}/convert/transportationManager`); const specialClass = require(`${libs}/convert/booleanVariable`); - const ids = { gender_id: gender, period_id: period, diff --git a/src/libs/middlewares/reqQueryFields.js b/src/libs/middlewares/reqQueryFields.js index 5b28567fcf5ded59647c712a2a3dbd17719bab00..61f989630effb5bd5a03519641eb0ca94bf781b0 100644 --- a/src/libs/middlewares/reqQueryFields.js +++ b/src/libs/middlewares/reqQueryFields.js @@ -240,13 +240,17 @@ class ReqQueryFields { if (Array.isArray(value.resultField)) { value.tableField.forEach((f, i) => { sql.field(table+'.'+f, value.resultField[i] || f) - .group(table+'.'+f) + if(!value.dontGroup) { + sql.group(table+'.'+f) .order(table+'.'+f); + } }) }else{ sql.field(table+'.'+value.tableField, value.resultField || value.tableField) - .order(table+'.'+value.tableField) + if(!value.dontGroup) { + sql.order(table+'.'+value.tableField) .group(table+'.'+value.tableField); + } } } // Se o valor é um campo para ser usado no WHERE diff --git a/src/libs/routes/classCount.js b/src/libs/routes/classCount.js new file mode 100644 index 0000000000000000000000000000000000000000..d415d5a3fdf40d15a70be2a15b505adbeed79901 --- /dev/null +++ b/src/libs/routes/classCount.js @@ -0,0 +1,407 @@ +const express = require('express'); + +const classCountApp = 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(); + +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: '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' + } +}).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: 'turma' + } +}, '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: 'turma' + } +}, 'filter'); + +classCountApp.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')); + +classCountApp.get('/years', (req, res, next) => { + req.sql.from('escola') + .field('DISTINCT escola.ano_censo', 'year'); + next(); +}, query, response('years')); + +classCountApp.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')); + +classCountApp.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')); + +classCountApp.get('/location', (req, res, next) => { + req.result = [ + {id: 1, name: 'Urbana'}, + {id: 2, name: 'Rural'} + ]; + next(); +}, response('location')); + +classCountApp.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')); + +classCountApp.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')); + +classCountApp.get('/education_level_short', (req, res, next) => { + req.result = []; + for(let i = 1; i <= 7; ++i) { + req.result.push({ + id: i, + name: id2str.educationLevelShort(i) + }); + } + req.result.push({ + id: 99, + name: id2str.educationLevelShort(99) + }); + next(); +}, response('education_level_short')); + +classCountApp.get('/source', (req, res, next) => { + req.sql.from('fonte') + .field('fonte', 'source') + .where('tabela = \'turma\''); + next(); +}, query, response('source')); + +function mediaCalc(response) { + let obj = []; + response.forEach((result) => { + let newObj = {}; + let keys = Object.keys(result); + keys.forEach((key) => { + if(key !== "total_classes" && key !== "total_enrollment") + newObj[key] = result[key] + }) + newObj.total = result.total_enrollment / result.total_classes; + obj.push(newObj); + }); + return(obj); +} + +classCountApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { + if(("education_level_mod" in req.dims) || ("education_level_mod" in req.filter)) { + req.sql.field('COUNT(turma.id)', 'total_classes') + .field('SUM(turma.num_matricula)', 'total_enrollment') + .field("'Brasil'", 'name') + .field('turma.ano_censo', 'year') + .from('turma') + .group('turma.ano_censo') + .order('turma.ano_censo') + .where('turma.tipo_turma_id = 0 AND turma.etapas_mod_ensino_segmento_id >= 1 AND turma.etapas_mod_ensino_segmento_id <= 10'); + next(); + } else { + res.status(400); + next({ + status: 400, + message: 'Wrong/No filter specified' + }); + } +}, rqf.build(), query, id2str.transform(), (req, res, next) => { + req.partial = req.result; + req.resetSql(); + req.dims = {}; + req.filter = {}; + req.sql.field('COUNT(turma.id)', 'total_classes') + .field('SUM(turma.num_matricula)', 'total_enrollment') + .field("'Brasil'", 'name') + .field('turma.ano_censo', 'year') + .from('turma') + .group('turma.ano_censo') + .order('turma.ano_censo') + .where('turma.tipo_turma_id = 0 AND turma.etapas_mod_ensino_segmento_id >= 1 AND turma.etapas_mod_ensino_segmento_id <= 10'); + next(); +}, query, addMissing(rqf), id2str.transform(), (req, res, next) => { + const classCount = mediaCalc(req.partial); + const yearClassCount = mediaCalc(req.result); + req.result = classCount; + yearClassCount.forEach((result) => { + let obj = {}; + obj = result; + obj.label = "total_year_media"; + req.result.push(obj); + }) + next(); +}, response('class_count')); + +classCountApp.get('/count', rqf.parse(), rqf.build(), (req, res, next) => { + req.sql.field('COUNT(turma.id)', 'total_classes') + .field('SUM(turma.num_matricula)', 'total_enrollment') + .field("'Brasil'", 'name') + .field('turma.ano_censo', 'year') + .from('turma') + .group('turma.ano_censo') + .order('turma.ano_censo') + .where('turma.tipo_turma_id = 0 AND turma.dependencia_adm_id <= 3 AND ((turma.etapa_resumida >= 1 AND turma.etapa_resumida <= 7) OR turma.etapa_resumida = 99)'); + next(); +}, query, id2str.transform(), (req, res, next) => { + req.partial = []; + if(Object.keys(req.dims).length > 0 || Object.keys(req.filter).length > 0) { + req.partial = req.result; + req.resetSql(); + req.dims = {}; + req.filter = {}; + req.sql.field('COUNT(turma.id)', 'total_classes') + .field('SUM(turma.num_matricula)', 'total_enrollment') + .field("'Brasil'", 'name') + .field('turma.ano_censo', 'year') + .from('turma') + .group('turma.ano_censo') + .order('turma.ano_censo') + .where('turma.tipo_turma_id = 0 AND turma.dependencia_adm_id <= 3 AND ((turma.etapa_resumida >= 1 AND turma.etapa_resumida <= 7) OR turma.etapa_resumida = 99)'); + } + next(); +}, query, addMissing(rqf), id2str.transform(), (req, res, next) => { + if(req.partial.length > 0) { + const classCount = mediaCalc(req.partial); + const yearClassCount = mediaCalc(req.result); + req.result = classCount; + yearClassCount.forEach((result) => { + let obj = {}; + obj = result; + obj.label = "total_year_media"; + req.result.push(obj); + }) + } + else { + const classCount = mediaCalc(req.result); + req.result = classCount; + } + next(); +}, response('class_count')); + +module.exports = classCountApp; diff --git a/src/libs/routes/classroomCount.js b/src/libs/routes/classroomCount.js index e3144507debb9c080a9893304bd50fd05eb68aa0..bdec29e54dca56e3f0eb71184e89645002edf0de 100644 --- a/src/libs/routes/classroomCount.js +++ b/src/libs/routes/classroomCount.js @@ -393,7 +393,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => { // Total de salas educationLevel.enrollment.total_classrooms_needed = (educationLevel.enrollment.full_period_classes + educationLevel.enrollment.day_classes/2); - if(educationLevel.enrollment.night_classes > educationLevel.enrollment.day_classes) educationLevel.enrollment.total_classrooms_needed += (educationLevel.enrollment.night_classes - educationLevel.enrollment.day_classes); + if(educationLevel.enrollment.night_classes > (educationLevel.enrollment.day_classes/2)) educationLevel.enrollment.total_classrooms_needed += (educationLevel.enrollment.night_classes - (educationLevel.enrollment.day_classes/2)); educationLevel.enrollment.total_classrooms_needed = Math.ceil(educationLevel.enrollment.total_classrooms_needed); diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js index 83ef3f9c8d531692849c5940bd9333604c787989..66268b11409cf918ce8de61779a16b5323669639 100644 --- a/src/libs/routes/enrollment.js +++ b/src/libs/routes/enrollment.js @@ -240,7 +240,6 @@ enrollmentApp.get('/age_range_all', (req, res, next) => { next(); }, response('age_range_all')); - rqf.addField({ name: 'filter', field: false, @@ -324,7 +323,22 @@ rqf.addField({ foreign: 'regiao_id', foreignTable: 'matricula' } -}).addValue({ +}).addValueToField({ + name: 'state', + table: 'estado', + tableField: ['nome', 'id'], + resultField: ['state_name', 'state_id'], + where: { + relation: '=', + type: 'integer', + field: 'id' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'matricula' + } +}, 'dims').addValueToField({ name: 'state', table: 'estado', tableField: 'nome', @@ -339,7 +353,7 @@ rqf.addField({ foreign: 'estado_id', foreignTable: 'matricula' } -}).addValueToField({ +}, 'filter').addValueToField({ name: 'city', table: 'municipio', tableField: ['nome', 'id'], @@ -516,6 +530,8 @@ enrollmentApp.get('/download', passport.authenticate('bearer', { session: false enrollmentApp.get('/diagnosis', rqf.parse(), (req, res, next) => { req.dims = {}; + req.dims.state = true; + req.dims.city = true; req.dims.school_year = true; req.dims.location = true; req.dims.adm_dependency_detailed = true; @@ -550,8 +566,12 @@ enrollmentApp.get('/diagnosis', rqf.parse(), (req, res, next) => { let i = 0; while(i < enrollments.length) { let enrollment = enrollments[i]; - let educationLevelHash = '' + enrollment.year + educationSchoolYear[enrollment.school_year_id].id; - let schoolYearHash = '' + enrollment.year + enrollment.school_year_id; + if(!educationSchoolYear[enrollment.school_year_id]) { + ++i; + continue; + } + let educationLevelHash = '' + enrollment.year + educationSchoolYear[enrollment.school_year_id].id + enrollment.city_id; + let schoolYearHash = '' + enrollment.year + enrollment.school_year_id + enrollment.city_id; let currentEducation = null; // Busca ou cria a etapa de ensino adequada @@ -571,6 +591,10 @@ enrollmentApp.get('/diagnosis', rqf.parse(), (req, res, next) => { let obj = { year: enrollment.year, name: enrollment.name, + state_id: enrollment.state_id, + state_name: enrollment.state_name, + city_id: enrollment.city_id, + city_name: enrollment.city_name, education_level_school_year_id: educationSchoolYear[enrollment.school_year_id].id, education_level_school_year_name: educationSchoolYear[enrollment.school_year_id].name, total: 0, @@ -612,6 +636,10 @@ enrollmentApp.get('/diagnosis', rqf.parse(), (req, res, next) => { let obj = { year: enrollment.year, name: enrollment.name, + state_id: enrollment.state_id, + state_name: enrollment.state_name, + city_id: enrollment.city_id, + city_name: enrollment.city_name, education_level_school_year_id: enrollment.school_year_id, education_level_school_year_name: enrollment.school_year_name, total: 0, @@ -719,6 +747,8 @@ enrollmentApp.get('/diagnosis', rqf.parse(), (req, res, next) => { enrollmentApp.get('/projection', rqf.parse(), (req, res, next) => { req.dims = {}; + req.dims.state = true; + req.dims.city = true; req.dims.location = true; req.dims.school_year = true; req.dims.adm_dependency = true; @@ -749,17 +779,23 @@ enrollmentApp.get('/projection', rqf.parse(), (req, res, next) => { } } + console.log(educationSchoolYear); + let result = []; let educationLevelSet = new Set(); let schoolYearSet = new Set(); let i = 0; while(i < enrollments.length) { let enrollment = enrollments[i]; - let educationLevelHash = '' + enrollment.year + educationSchoolYear[enrollment.school_year_id].id; - let schoolYearHash = '' + enrollment.year + enrollment.school_year_id; + if(!educationSchoolYear[enrollment.school_year_id]) { + ++i; + continue; + } + let educationLevelHash = '' + enrollment.year + educationSchoolYear[enrollment.school_year_id].id + enrollment.city_id; + let schoolYearHash = '' + enrollment.year + enrollment.school_year_id + enrollment.city_id; let currentEducation = null; - // Busca ou cria a etada de ensino adequada + // Busca ou cria a etapa de ensino adequada if(educationLevelSet.has(educationLevelHash)) { let j = 0; let edu = result[j]; @@ -776,6 +812,10 @@ enrollmentApp.get('/projection', rqf.parse(), (req, res, next) => { let obj = { year: enrollment.year, name: enrollment.name, + state_id: enrollment.state_id, + state_name: enrollment.state_name, + city_id: enrollment.city_id, + city_name: enrollment.city_name, education_level_school_year_id: educationSchoolYear[enrollment.school_year_id].id, education_level_school_year_name: educationSchoolYear[enrollment.school_year_id].name, urban_day_total: 0, @@ -805,6 +845,10 @@ enrollmentApp.get('/projection', rqf.parse(), (req, res, next) => { let obj = { year: enrollment.year, name: enrollment.name, + state_id: enrollment.state_id, + state_name: enrollment.state_name, + city_id: enrollment.city_id, + city_name: enrollment.city_name, education_level_school_year_id: enrollment.school_year_id, education_level_school_year_name: enrollment.school_year_name, urban_day_total: 0, diff --git a/src/libs/routes/school.js b/src/libs/routes/school.js index 33b5e1cf983e146e30c928f50a22af6d1f98e813..830697742406491451539276261c468cc4024f04 100644 --- a/src/libs/routes/school.js +++ b/src/libs/routes/school.js @@ -219,7 +219,44 @@ rqf.addField({ field: 'ano_censo', table: 'escola' } -}); +}).addField({ + name: 'search', + field: true, + where: true +}).addValueToField({ + name: 'city_name', + table: 'municipio', + tableField: 'nome', + resultField: 'city_name', + dontGroup: true, + where: { + relation: 'LIKE', + type: 'string', + field: 'nome' + }, + join: { + primary: 'id', + foreign: 'municipio_id', + foreignTable: 'escola' + } +}, 'search') +.addValueToField({ + name: 'state_name', + table: 'estado', + tableField: 'nome', + resultField: 'state_name', + dontGroup: true, + where: { + relation: 'LIKE', + type: 'string', + field: 'sigla' + }, + join: { + primary: 'id', + foreign: 'estado_id', + foreignTable: 'escola' + } +}, 'search'); rqfCount.addField({ name: 'filter', @@ -464,13 +501,7 @@ rqfCount.addField({ } }); 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') diff --git a/src/libs/routes/transport.js b/src/libs/routes/transport.js index 3b6b38bfea68f857f09a62a6cff1e9edcaa0eb4c..04062b1ace6e1a26117b706d3a1733c984429631 100644 --- a/src/libs/routes/transport.js +++ b/src/libs/routes/transport.js @@ -360,7 +360,6 @@ transportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => { .where('matricula.tipo <= 3'); req.queryIndex.allEnrollmentTransport = req.querySet.push(allEnrollmentTransport) - 1; - let allTransports = req.sql.clone() allTransports.field('COUNT(*)', 'total') .field("'Brasil'", 'name')