From fbfa487f62471974d68326506fd868ac1a4044ae Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Tue, 7 Mar 2017 11:17:00 -0300
Subject: [PATCH 01/30] Change city route to new db schema

---
 src/libs/routes/city.js | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/libs/routes/city.js b/src/libs/routes/city.js
index 3b03ef9c..b7999a8e 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'));
 
-- 
GitLab


From 8278571354f3366dc1f5f89422ffb9fdf89859f1 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Tue, 7 Mar 2017 11:17:19 -0300
Subject: [PATCH 02/30] Change region route to new db schema

---
 src/libs/routes/region.js | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/libs/routes/region.js b/src/libs/routes/region.js
index f152d899..a752fa1b 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'));
-- 
GitLab


From ffe4b7c0cfe82146ad2408393b195aab0853e263 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Tue, 7 Mar 2017 11:17:45 -0300
Subject: [PATCH 03/30] Change school route to new db schema

---
 src/libs/routes/school.js | 33 ++++++++++++++++++++++-----------
 1 file changed, 22 insertions(+), 11 deletions(-)

diff --git a/src/libs/routes/school.js b/src/libs/routes/school.js
index 6420db5d..1d44d771 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,10 @@ 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.estado_id', 'state_id')
+        .field('escola.municipio_id', 'city_id');
     next();
 }, query, response('school'));
 
-- 
GitLab


From 56abd94e804f53e7eb08e452628a0a96292ff06e Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Tue, 7 Mar 2017 11:18:02 -0300
Subject: [PATCH 04/30] Change state route to new schema

---
 src/libs/routes/state.js | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/libs/routes/state.js b/src/libs/routes/state.js
index 4da63260..2663fe98 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')
-- 
GitLab


From 3c9b05a151b68b27ea3294c4221a5db8ca29aaf9 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Tue, 7 Mar 2017 11:18:26 -0300
Subject: [PATCH 05/30] Start change in enrollment route to new db schema

---
 src/libs/routes/enrollment.js | 101 ++++++++++++++--------------------
 1 file changed, 42 insertions(+), 59 deletions(-)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 74ba538a..d08025c9 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();
@@ -37,18 +36,16 @@ 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('id', 'id')
     .field('desc_etapa', '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', 'id')
     .field('nome', 'name');
-
     next();
 }, query, response('adm_dependency'));
 
@@ -68,12 +65,12 @@ 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: 'education_level',
@@ -83,12 +80,12 @@ rqf.addField({
     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: 'etapa_ensino_id',
+        foreignTable: 'matricula'
     }
 }).addValue({
     name: 'region',
@@ -98,12 +95,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: 'cod_regiao',
+        foreignTable: 'matricula'
     }
 }).addValue({
     name: 'state',
@@ -113,12 +110,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,12 +125,12 @@ 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',
@@ -143,12 +140,12 @@ rqf.addField({
     where: {
         relation: '=',
         type: 'integer',
-        field: 'cod_entidade'
+        field: 'id'
     },
     join: {
-        primary: 'cod_entidade',
-        foreign: 'cod_entidade',
-        foreignTable: 'turma'
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'matricula'
     }
 }).addValue({
     name: 'location',
@@ -158,31 +155,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 +174,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'max_year',
-    table: 'turma',
+    table: 'matricula',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -204,12 +186,13 @@ rqf.addField({
 
 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, response('enrollment'));
 
-- 
GitLab


From b733e01ad9f0668324f4fce6d409d4b872d85512 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 8 Mar 2017 13:06:05 -0300
Subject: [PATCH 06/30] Fix 304 with browser reloads

---
 src/libs/app.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/libs/app.js b/src/libs/app.js
index 7fea0e03..97c2683a 100644
--- a/src/libs/app.js
+++ b/src/libs/app.js
@@ -34,6 +34,10 @@ app.use((req, res, next) => {
     req.sql = squel.select();
     next();
 });
+app.use((req, res, next) => {
+    res.setHeader('Last-Modified', (new Date()).toUTCString());
+    next();
+});
 // Mounts all API routes under /api/v1
 app.use('/api/v1', api);
 
-- 
GitLab


From 1c8c1eb67fbeb370eff74b31d60965aca8f99be9 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Thu, 16 Mar 2017 11:07:50 -0300
Subject: [PATCH 07/30] Add gender and ethnic_group to enrollment params

* Create routes /enrollment/gender and /enrollment/ethnic_group
* Add gender and ethnic_group to enrollment params in RQF
---
 gulpfile.babel.js             |  6 ++--
 src/libs/routes/enrollment.js | 60 +++++++++++++++++++++++++++++------
 src/test/enrollment.js        |  6 ++--
 src/test/query.js             |  2 +-
 4 files changed, 58 insertions(+), 16 deletions(-)

diff --git a/gulpfile.babel.js b/gulpfile.babel.js
index e75f65bf..a6eb1a87 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: 30000}))
     .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/routes/enrollment.js b/src/libs/routes/enrollment.js
index d08025c9..437c58d3 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -35,9 +35,9 @@ enrollmentApp.get('/location', (req, res, next) => {
 
 // Returns all educational levels avaible
 enrollmentApp.get('/education_level', (req, res, next) => {
-    req.sql.from('etapa_ensino')
-    .field('id', 'id')
-    .field('desc_etapa', 'name');
+    req.sql.from('serie_ano')
+    .field('id')
+    .field('nome', 'name');
     next();
 }, query, response('education_level'));
 
@@ -49,6 +49,28 @@ enrollmentApp.get('/adm_dependency', (req, res, next) => {
     next();
 }, query, response('adm_dependency'));
 
+// Return genders
+enrollmentApp.get('/gender', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Masculino'},
+        {id: 2, name: 'Feminino'}
+    ];
+    next();
+}, response('gender'));
+
+// Return ethnic group
+enrollmentApp.get('/ethnic_group', (req, res, next) => {
+    req.result = [
+        {id: 0, name: 'Não declarada'},
+        {id: 1, name: 'Branca'},
+        {id: 2, name: 'Preta'},
+        {id: 3, name: 'Parda'},
+        {id: 4, name: 'Amarela'},
+        {id: 5, name: 'Indígena'}
+    ];
+    next();
+}, response('ethnic_group'));
+
 rqf.addField({
     name: 'filter',
     field: false,
@@ -74,9 +96,9 @@ rqf.addField({
     }
 }).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',
@@ -84,7 +106,7 @@ rqf.addField({
     },
     join: {
         primary: 'id',
-        foreign: 'etapa_ensino_id',
+        foreign: 'serie_ano_id',
         foreignTable: 'matricula'
     }
 }).addValue({
@@ -99,7 +121,7 @@ rqf.addField({
     },
     join: {
         primary: 'id',
-        foreign: 'cod_regiao',
+        foreign: 'regiao_id',
         foreignTable: 'matricula'
     }
 }).addValue({
@@ -135,7 +157,7 @@ rqf.addField({
 }).addValue({
     name: 'school',
     table: 'escola',
-    tableField: 'cod_entidade',
+    tableField: 'nome_escola',
     resultField: 'school_name',
     where: {
         relation: '=',
@@ -182,6 +204,26 @@ 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: 'matricula',
+    tableField: 'cor_raca',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ethnic_group_id'
+    }
 });
 
 enrollmentApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
diff --git a/src/test/enrollment.js b/src/test/enrollment.js
index b40c2de8..3c19f29e 100644
--- a/src/test/enrollment.js
+++ b/src/test/enrollment.js
@@ -155,7 +155,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&filter=min_year:2015,max_year:2015,city:4106902')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -163,8 +163,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 6d345445..ed492e8a 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)=>{
-- 
GitLab


From 8aa5528f2df2b81afba004e1d73fbeaf7babc737 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Fri, 17 Mar 2017 10:12:03 -0300
Subject: [PATCH 08/30] Add adm_dependency_detailed to enrollment route

---
 src/libs/routes/enrollment.js | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 437c58d3..6f5a623c 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -43,11 +43,19 @@ enrollmentApp.get('/education_level', (req, res, next) => {
 
 // Returns all adm dependencies
 enrollmentApp.get('/adm_dependency', (req, res, next) => {
+    req.sql.from('dependencia_adm')
+    .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'));
+}, query, response('adm_dependency_detailed'));
 
 // Return genders
 enrollmentApp.get('/gender', (req, res, next) => {
@@ -94,6 +102,21 @@ rqf.addField({
         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: 'serie_ano',
-- 
GitLab


From 282249c9948a3db30c5260242cdc18c71fc2cafb Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Fri, 17 Mar 2017 10:12:44 -0300
Subject: [PATCH 09/30] Add composite foreign key habilities to RQF

Now you can join tables with composite foreign/primary keys using RQF. In the
join attribute just use an array of columns that need to be used in the join
---
 src/libs/middlewares/reqQueryFields.js | 14 +++++++++++++-
 src/libs/routes/enrollment.js          |  4 ++--
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/libs/middlewares/reqQueryFields.js b/src/libs/middlewares/reqQueryFields.js
index 1fe597d3..a383ade2 100644
--- a/src/libs/middlewares/reqQueryFields.js
+++ b/src/libs/middlewares/reqQueryFields.js
@@ -201,7 +201,19 @@ class ReqQueryFields {
                             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;
diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 6f5a623c..af0904ab 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -188,8 +188,8 @@ rqf.addField({
         field: 'id'
     },
     join: {
-        primary: 'id',
-        foreign: 'escola_id',
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
         foreignTable: 'matricula'
     }
 }).addValue({
-- 
GitLab


From 1a4af79bce0f47b413af4c209567a4d914a6e9f9 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Fri, 17 Mar 2017 10:45:36 -0300
Subject: [PATCH 10/30] Change hsaJoined hash in RQF to use table name

---
 src/libs/middlewares/reqQueryFields.js | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/src/libs/middlewares/reqQueryFields.js b/src/libs/middlewares/reqQueryFields.js
index a383ade2..6a194c84 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,18 +182,11 @@ 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
@@ -215,8 +204,7 @@ class ReqQueryFields {
                             }
                             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) {
-- 
GitLab


From 17ea666b4516c3215fc472499985094004807d18 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Tue, 21 Mar 2017 09:41:35 -0300
Subject: [PATCH 11/30] Add period to enrollment

---
 src/libs/routes/enrollment.js | 35 ++++++++++++++++++++++++++---------
 1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index af0904ab..0782b8bb 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -68,16 +68,18 @@ enrollmentApp.get('/gender', (req, res, next) => {
 
 // Return ethnic group
 enrollmentApp.get('/ethnic_group', (req, res, next) => {
-    req.result = [
-        {id: 0, name: 'Não declarada'},
-        {id: 1, name: 'Branca'},
-        {id: 2, name: 'Preta'},
-        {id: 3, name: 'Parda'},
-        {id: 4, name: 'Amarela'},
-        {id: 5, name: 'Indígena'}
-    ];
+    req.sql.from('cor_raca')
+    .field('id')
+    .field('nome', 'name');
     next();
-}, response('ethnic_group'));
+}, 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',
@@ -247,6 +249,21 @@ rqf.addField({
         type: 'integer',
         field: 'ethnic_group_id'
     }
+}).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) => {
-- 
GitLab


From 59e74fd2151947cde5342f76f4ef91674e10eb01 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Tue, 21 Mar 2017 09:43:01 -0300
Subject: [PATCH 12/30] :green_heart: Fix database in config.json for this
 branch

---
 .gitlab-ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 04cdb1f9..90d49bbe 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:
-- 
GitLab


From 800cb84165bc072322e63124bfe8a2540470b2c5 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 09:48:01 -0300
Subject: [PATCH 13/30] Change /location/sociodemographic to
 /spatial/sociodemographic

Also included RQF. And /spatial/sociodemographic is usins RQF to build queries
---
 src/libs/routes/api.js                      |   4 +-
 src/libs/routes/{location.js => spatial.js} | 416 ++++----------------
 src/test/{location.js => spatial.js}        |  26 +-
 3 files changed, 84 insertions(+), 362 deletions(-)
 rename src/libs/routes/{location.js => spatial.js} (72%)
 rename src/test/{location.js => spatial.js} (97%)

diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js
index 2c29a246..ce905d8b 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/location.js b/src/libs/routes/spatial.js
similarity index 72%
rename from src/libs/routes/location.js
rename to src/libs/routes/spatial.js
index f5476f5e..34241b31 100644
--- a/src/libs/routes/location.js
+++ b/src/libs/routes/spatial.js
@@ -12,7 +12,11 @@ const sqlQuery = require(`${libs}/db/query_exec`);
 
 const response = require(`${libs}/middlewares/response`);
 
-const locationApp = express();
+const spatialApp = express();
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+let rqf = new ReqQueryFields();
 
 function locationIdToStr(locationId) {
     let locationStr = 'Total';
@@ -134,12 +138,60 @@ function dbExecAll(querySet = []) {
     return querySet.map((qry) => { return sqlQuery(qry.toString()); });
 }
 
-locationApp.get('/sociodemographic', (req, res, next) => {
+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: 'estado_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'
+    }
+});
+
+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 = squel.select()
+    const populationQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
         .field('SUM(populacao)', 'population')
         .field('ibge_populacao.ano_censo', 'census_year')
@@ -151,7 +203,7 @@ locationApp.get('/sociodemographic', (req, res, next) => {
         .field('MAX(ibge_pib.ano_censo)')
         .from('ibge_pib');
 
-    const pibQry = squel.select()
+    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')
@@ -163,7 +215,7 @@ locationApp.get('/sociodemographic', (req, res, next) => {
         .field('MAX(adh_idh.ano_censo)')
         .from('adh_idh');
 
-    const idhQry = squel.select()
+    const idhQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
         .field('AVG(idhm)', 'idhm')
         .field('adh_idh.ano_censo', 'census_year')
@@ -175,7 +227,7 @@ locationApp.get('/sociodemographic', (req, res, next) => {
         .field('MAX(adh_analfabetismo.ano_censo)')
         .from('adh_analfabetismo');
 
-    const analfabQry = squel.select()
+    const analfabQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
         .field('AVG(t_analf15m)', 'analfabetism')
         .field('adh_analfabetismo.ano_censo', 'census_year')
@@ -187,7 +239,7 @@ locationApp.get('/sociodemographic', (req, res, next) => {
         .field('MAX(adh_gini.ano_censo)')
         .from('adh_gini');
 
-    const giniQry = squel.select()
+    const giniQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
         .field('AVG(gini)', 'gini')
         .field('adh_gini.ano_censo', 'census_year')
@@ -206,339 +258,9 @@ locationApp.get('/sociodemographic', (req, res, next) => {
         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'));
+}, response('spatial'));
 
-locationApp.get('/educational', (req, res, next) => {
+spatialApp.get('/educational', (req, res, next) => {
     const censusYearQry = squel.select()
         .field('MAX(escola.ano_censo)', 'ano_censo')
         .from('escola')
@@ -630,7 +352,7 @@ locationApp.get('/educational', (req, res, next) => {
     });
 }, response('location'));
 
-locationApp.get('/educational/region/:id', (req, res, next) => {
+spatialApp.get('/educational/region/:id', (req, res, next) => {
     const regionId = parseInt(req.params.id, 10);
 
     const censusYearQry = squel.select()
@@ -753,7 +475,7 @@ locationApp.get('/educational/region/:id', (req, res, next) => {
     });
 }, response('location'));
 
-locationApp.get('/educational/state/:id', (req, res, next) => {
+spatialApp.get('/educational/state/:id', (req, res, next) => {
     const stateId = parseInt(req.params.id, 10);
 
     const censusYearQry = squel.select()
@@ -872,7 +594,7 @@ locationApp.get('/educational/state/:id', (req, res, next) => {
     });
 }, response('location'));
 
-locationApp.get('/educational/city/:id', (req, res, next) => {
+spatialApp.get('/educational/city/:id', (req, res, next) => {
     const cityId = parseInt(req.params.id, 10);
 
     const censusYearQry = squel.select()
@@ -991,7 +713,7 @@ locationApp.get('/educational/city/:id', (req, res, next) => {
     });
 }, response('location'));
 
-locationApp.get('/educational/school_level', (req, res, next) => {
+spatialApp.get('/educational/school_level', (req, res, next) => {
     const enrollmentsPerSchoolLevelYearQry = squel.select()
         .field('MAX(turma.ano_censo)', 'census_year')
         .from('turma');
@@ -1057,7 +779,7 @@ locationApp.get('/educational/school_level', (req, res, next) => {
     });
 }, response('location'));
 
-locationApp.get('/educational/school_level/region/:id', (req, res, next) => {
+spatialApp.get('/educational/school_level/region/:id', (req, res, next) => {
     const regionId = parseInt(req.params.id, 10);
 
     const enrollmentsPerSchoolLevelYearQry = squel.select()
@@ -1130,7 +852,7 @@ locationApp.get('/educational/school_level/region/:id', (req, res, next) => {
     });
 }, response('location'));
 
-locationApp.get('/educational/school_level/state/:id', (req, res, next) => {
+spatialApp.get('/educational/school_level/state/:id', (req, res, next) => {
     const stateId = parseInt(req.params.id, 10);
 
     const enrollmentsPerSchoolLevelYearQry = squel.select()
@@ -1203,7 +925,7 @@ locationApp.get('/educational/school_level/state/:id', (req, res, next) => {
     });
 }, response('location'));
 
-locationApp.get('/educational/school_level/city/:id', (req, res, next) => {
+spatialApp.get('/educational/school_level/city/:id', (req, res, next) => {
     const cityId = parseInt(req.params.id, 10);
 
     const enrollmentsPerSchoolLevelYearQry = squel.select()
@@ -1276,4 +998,4 @@ locationApp.get('/educational/school_level/city/:id', (req, res, next) => {
     });
 }, response('location'));
 
-module.exports = locationApp;
+module.exports = spatialApp;
diff --git a/src/test/location.js b/src/test/spatial.js
similarity index 97%
rename from src/test/location.js
rename to src/test/spatial.js
index 42761a8d..b0e53083 100644
--- a/src/test/location.js
+++ b/src/test/spatial.js
@@ -26,10 +26,10 @@ chai.use(chaiHttp);
 
 const testTimeout = 5000;
 
-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
@@ -254,7 +254,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/region/1')
             .end((err, res) => {
                 res.should.have.status(200);
                 // test response format
@@ -319,7 +319,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/state/42')
             .end((err, res) => {
                 res.should.have.status(200);
                 // test response format
@@ -384,7 +384,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/city/4106902')
             .end((err, res) => {
                 res.should.have.status(200);
                 // test response format
@@ -449,7 +449,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 +478,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/region/1')
             .end((err, res) => {
                 res.should.have.status(200);
                 // test response format
@@ -507,7 +507,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/state/42')
             .end((err, res) => {
                 res.should.have.status(200);
                 // test response format
@@ -536,7 +536,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/state/4106902')
             .end((err, res) => {
                 res.should.have.status(200);
                 // test response format
-- 
GitLab


From 4682b2511012d6ff9aa842f6a603e879a843b244 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 09:56:54 -0300
Subject: [PATCH 14/30] Change /spatial/educational to use RQF query builder

---
 src/libs/routes/spatial.js | 436 ++++---------------------------------
 1 file changed, 37 insertions(+), 399 deletions(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index 34241b31..d3f4e9b2 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -260,437 +260,75 @@ spatialApp.get('/sociodemographic', rqf.parse(), rqf.build(), (req, res, next) =
     });
 }, response('spatial'));
 
-spatialApp.get('/educational', (req, res, next) => {
+spatialApp.get('/educational', req.parse(), rqf.build(), (req, res, next) => {
     const censusYearQry = squel.select()
         .field('MAX(escola.ano_censo)', 'ano_censo')
         .from('escola')
         .toString();
 
-    const totalSchoolsQry = squel.select()
+    const totalSchoolsQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
         .field('0', 'location')
-        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('COUNT(DISTINCT(escola.id))', 'total')
         .field('escola.ano_censo', 'census_year')
         .from('escola')
+        .join('turma', null, 'turma.ano_censo=escola.ano_censo AND turma.escola_id = escola.id')
         .where(`escola.ano_censo IN (${censusYearQry})`)
-        .where('escola.id_tipo_turma = 0')
+        .where('turma.tipo_turma_id = 0')
         .group('escola.ano_censo');
 
-    const schoolsPerLocationQry = squel.select()
+    const schoolsPerLocationQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
-        .field('escola.tipo_localizacao', 'location')
+        .field('escola.cod_localizacao', 'location')
         .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
         .field('escola.ano_censo', 'census_year')
         .from('escola')
+        .join('turma', null, 'turma.ano_censo=escola.ano_censo AND turma.escola_id=escola.id')
         .where(`escola.ano_censo IN (${censusYearQry})`)
-        .where('escola.id_tipo_turma = 0')
-        .group('escola.tipo_localizacao')
+        .where('truma.tipo_turma_id = 0')
+        .group('escola.cod_localizacao')
         .group('escola.ano_censo')
-        .order('escola.tipo_localizacao');
+        .order('escola.cod_localizacao');
 
     const schoolClassYearQry = squel.select()
-        .field('MAX(turma.ano_censo)')
+        .field('MAX(matricula.ano_censo)')
         .from('turma')
         .toString();
 
-    const enrollmentsQry = squel.select()
+    const enrollmentsQry = req.sql.clone()
         .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');
+        .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 = 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')
+        .field('COALESCE(COUNT(matricula.id), 0)', 'total')
+        .field('matricula.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency_name')
+        .from('matricula')
+        .join('dependencia_adm', null, '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 = 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'));
-
-spatialApp.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'));
-
-spatialApp.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'));
-
-spatialApp.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')
+        .field('COALESCE(SUM(matricula.id), 0)', 'total')
+        .field('matricula.ano_censo', 'census_year')
+        .field('etapa_ensino.desc_etapa', 'school_level_name')
+        .from('matricula')
+        .join('etapa_ensino', null, '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.pk_etapa_ensino_id')
-        .group('municipio.nome')
-        .order('municipio.nome')
-        .order('etapa_ensino.pk_etapa_ensino_id');
+        .order('etapa_ensino.desc_etapa');
 
     const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep",
         "enrollment_per_school_level" ];
@@ -711,7 +349,7 @@ spatialApp.get('/educational/city/:id', (req, res, next) => {
         log.error(`[SQL query error] ${error}`);
         next(error);
     });
-}, response('location'));
+}, response('spatial'));
 
 spatialApp.get('/educational/school_level', (req, res, next) => {
     const enrollmentsPerSchoolLevelYearQry = squel.select()
-- 
GitLab


From abff7df55b8d70c8e1ea60546cb8870d50979b5a Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 10:27:33 -0300
Subject: [PATCH 15/30] Fix small typo

---
 src/libs/routes/spatial.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index d3f4e9b2..a520ecca 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -260,7 +260,7 @@ spatialApp.get('/sociodemographic', rqf.parse(), rqf.build(), (req, res, next) =
     });
 }, response('spatial'));
 
-spatialApp.get('/educational', req.parse(), rqf.build(), (req, res, next) => {
+spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
     const censusYearQry = squel.select()
         .field('MAX(escola.ano_censo)', 'ano_censo')
         .from('escola')
-- 
GitLab


From 00b8018cda391443c926c3fbb0f45d817d13eb51 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 10:52:31 -0300
Subject: [PATCH 16/30] Fix typo in /spatial

---
 src/libs/routes/spatial.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index a520ecca..cfeef3cf 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -280,12 +280,12 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
     const schoolsPerLocationQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
         .field('escola.cod_localizacao', 'location')
-        .field('COUNT(DISTINCT(escola.cod_entidade))', 'total')
+        .field('COUNT(DISTINCT(escola.id))', 'total')
         .field('escola.ano_censo', 'census_year')
         .from('escola')
         .join('turma', null, 'turma.ano_censo=escola.ano_censo AND turma.escola_id=escola.id')
         .where(`escola.ano_censo IN (${censusYearQry})`)
-        .where('truma.tipo_turma_id = 0')
+        .where('turma.tipo_turma_id = 0')
         .group('escola.cod_localizacao')
         .group('escola.ano_censo')
         .order('escola.cod_localizacao');
-- 
GitLab


From 758d3f0d58d9bd91abc7490ca95d26ba8967011d Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 10:52:48 -0300
Subject: [PATCH 17/30] Add conversion from ids to names in /enrollment

Conversion added to gender, period and ethnic group
---
 src/libs/routes/enrollment.js | 60 ++++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 0782b8bb..55fcfa6e 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -276,6 +276,64 @@ enrollmentApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     .order('matricula.ano_censo')
     .where('matricula.tipo=0 OR matricula.tipo=1 OR matricula.tipo=2 OR matricula.tipo=3');
     next();
-}, query, response('enrollment'));
+}, 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.ethnic_group_id !== 'undefined') {
+            switch (result.ethnic_group) {
+                case 0:
+                    result.ethnic_group_name = 'Não declarada';
+                    break;
+                case 1:
+                    result.ethnic_group_name = 'Branca';
+                    break;
+                case 2:
+                    result.ethnic_group_name = 'Preta';
+                    break;
+                case 3:
+                    result.ethnic_group_name = 'Parda';
+                    break;
+                case 4:
+                    result.ethnic_group_name = 'Amarela';
+                    break;
+                case 5:
+                    result.ethnic_group_name = 'Indígena';
+                    break;
+                default:
+                    result.ethnic_group_name = 'Não declarada';
+                    break;
+            }
+            delete result.ethnic_group_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;
+        }
+    });
+}, response('enrollment'));
 
 module.exports = enrollmentApp;
-- 
GitLab


From ae51e40d4aa02388e2a2e0874c57851f26812f73 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 10:55:30 -0300
Subject: [PATCH 18/30] Add fogotten 'next()' in /enrollment

---
 src/libs/routes/enrollment.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 55fcfa6e..2481892e 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -334,6 +334,7 @@ enrollmentApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
             delete result.period_id;
         }
     });
+    next();
 }, response('enrollment'));
 
 module.exports = enrollmentApp;
-- 
GitLab


From 2db7a49d08aca71b21786939ace1d76a5f4137c4 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 10:59:35 -0300
Subject: [PATCH 19/30] Add join to ethnic_group

---
 src/libs/routes/enrollment.js | 39 ++++++++---------------------------
 1 file changed, 9 insertions(+), 30 deletions(-)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 2481892e..743a8443 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -241,13 +241,18 @@ rqf.addField({
     }
 }).addValue({
     name: 'ethnic_group',
-    table: 'matricula',
-    tableField: 'cor_raca',
-    resultField: 'ethnic_group_id',
+    table: 'cor_raca',
+    tableField: 'name',
+    resultField: 'ethnic_group_name',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'ethnic_group_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cor_raca_id',
+        foreignTable: 'matricula'
     }
 }).addValue({
     name: 'period',
@@ -290,32 +295,6 @@ enrollmentApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
             }
             delete result.gender_id;
         }
-        if(typeof result.ethnic_group_id !== 'undefined') {
-            switch (result.ethnic_group) {
-                case 0:
-                    result.ethnic_group_name = 'Não declarada';
-                    break;
-                case 1:
-                    result.ethnic_group_name = 'Branca';
-                    break;
-                case 2:
-                    result.ethnic_group_name = 'Preta';
-                    break;
-                case 3:
-                    result.ethnic_group_name = 'Parda';
-                    break;
-                case 4:
-                    result.ethnic_group_name = 'Amarela';
-                    break;
-                case 5:
-                    result.ethnic_group_name = 'Indígena';
-                    break;
-                default:
-                    result.ethnic_group_name = 'Não declarada';
-                    break;
-            }
-            delete result.ethnic_group_id;
-        }
         if(typeof result.period_id !== 'undefined') {
             switch (result.period_id) {
                 case 1:
-- 
GitLab


From 40f4db14666ea1f675ed0ef2d2b16421d2420252 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 11:00:48 -0300
Subject: [PATCH 20/30] Fix typo in /enrollment

---
 src/libs/routes/enrollment.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 743a8443..e8664c4f 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -242,7 +242,7 @@ rqf.addField({
 }).addValue({
     name: 'ethnic_group',
     table: 'cor_raca',
-    tableField: 'name',
+    tableField: 'nome',
     resultField: 'ethnic_group_name',
     where: {
         relation: '=',
-- 
GitLab


From 8da95fa1a03ee66440b93c274aec8b1a7b325ca8 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 11:11:04 -0300
Subject: [PATCH 21/30] Fix typo (and GDK error in query execution)

---
 src/libs/routes/spatial.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index cfeef3cf..ffda3cee 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -292,7 +292,7 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
 
     const schoolClassYearQry = squel.select()
         .field('MAX(matricula.ano_censo)')
-        .from('turma')
+        .from('matricula')
         .toString();
 
     const enrollmentsQry = req.sql.clone()
-- 
GitLab


From 8ecf8b268f5dc25f219e9dbf98e3d6d20684a32b Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 11:22:40 -0300
Subject: [PATCH 22/30] Change /spatial/educational/school_level to use RQF

---
 src/libs/routes/spatial.js | 257 +++----------------------------------
 1 file changed, 18 insertions(+), 239 deletions(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index ffda3cee..82daae57 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -351,247 +351,26 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
     });
 }, response('spatial'));
 
-spatialApp.get('/educational/school_level', (req, res, next) => {
+spatialApp.get('/educational/school_level', rqf.parse(), rqf.build(), (req, res, next) => {
     const enrollmentsPerSchoolLevelYearQry = squel.select()
-        .field('MAX(turma.ano_censo)', 'census_year')
-        .from('turma');
+        .field('MAX(matricula.ano_censo)', 'census_year')
+        .from('matricula');
 
-    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'));
-
-spatialApp.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'));
-
-spatialApp.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'));
-
-spatialApp.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')
+    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_name')
+        .from('matricula')
+        .join('etapa_ensino', null, 'matricula.etapa_ensino_id=etapa_ensino.id')
+        .where(`matricula.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`)
+        .where('matricula.tipo <= 3')
         .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');
+        .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 ];
@@ -634,6 +413,6 @@ spatialApp.get('/educational/school_level/city/:id', (req, res, next) => {
         log.error(`[SQL query error] ${error}`);
         next(error);
     });
-}, response('location'));
+}, response('spatial'));
 
 module.exports = spatialApp;
-- 
GitLab


From b602c01d605763b927a98cf19959e9e131337bbc Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Wed, 22 Mar 2017 11:24:16 -0300
Subject: [PATCH 23/30] :fire: remove order by etapa_ensino.id from
 /spatial/educationl/school_level

---
 src/libs/routes/spatial.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index 82daae57..f8a56213 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -368,7 +368,6 @@ spatialApp.get('/educational/school_level', rqf.parse(), rqf.build(), (req, res,
         .group('etapa_ensino.desc_etapa')
         .group('matricula.serie_ano_id')
         .group('matricula.ano_censo')
-        .order('etapa_ensino.id')
         .order('matricula.serie_ano_id')
         .order('matricula.ano_censo');
 
-- 
GitLab


From 48c999e1b6db41ec27cd1deb7f56edf8ada8dd9b Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Thu, 23 Mar 2017 10:14:04 -0300
Subject: [PATCH 24/30] Fix SQL in /spatial

---
 src/libs/routes/spatial.js | 93 ++++++++++++++++++--------------------
 1 file changed, 43 insertions(+), 50 deletions(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index f8a56213..58473b0e 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -12,34 +12,13 @@ const sqlQuery = require(`${libs}/db/query_exec`);
 
 const response = require(`${libs}/middlewares/response`);
 
-const spatialApp = express();
-
 const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
 
-let rqf = new ReqQueryFields();
-
-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;
+const spatialApp = express();
 
-    }
-    return locationStr;
-}
+let rqf = new ReqQueryFields();
 
-function schoolYearIdToStr(schoolYearId)
-{
+function schoolYearIdToStr(schoolYearId) {
     let schoolYearStr;
     switch(schoolYearId) {
         case 11:
@@ -160,7 +139,7 @@ rqf.addField({
     name: 'state',
     table: 'estado',
     tableField: 'nome',
-    resultField: 'estado_name',
+    resultField: 'state_name',
     where: {
         relation: '=',
         type: 'integer',
@@ -184,6 +163,16 @@ rqf.addField({
         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) => {
@@ -268,31 +257,36 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
 
     const totalSchoolsQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
-        .field('0', 'location')
+        .field('\'Total\'', 'location_name')
         .field('COUNT(DISTINCT(escola.id))', 'total')
         .field('escola.ano_censo', 'census_year')
         .from('escola')
-        .join('turma', null, 'turma.ano_censo=escola.ano_censo AND turma.escola_id = escola.id')
+        .from('turma')
+        .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('escola.cod_localizacao', 'location')
         .field('COUNT(DISTINCT(escola.id))', 'total')
         .field('escola.ano_censo', 'census_year')
+        .field('localizacao.descricao', 'location_name')
         .from('escola')
-        .join('turma', null, 'turma.ano_censo=escola.ano_censo AND turma.escola_id=escola.id')
+        .from('localizacao')
+        .where('escola.cod_localizacao=localizacao.id')
+        .from('turma')
+        .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')
-        .order('escola.cod_localizacao');
+        .group('localizacao.descricao')
+        .order('localizacao.descricao');
 
     const schoolClassYearQry = squel.select()
-        .field('MAX(matricula.ano_censo)')
-        .from('matricula')
+        .field('MAX(turma.ano_censo)')
+        .from('turma')
         .toString();
 
     const enrollmentsQry = req.sql.clone()
@@ -301,34 +295,37 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('matricula.ano_censo', 'census_year')
         .from('matricula')
         .where(`matricula.ano_censo IN (${schoolClassYearQry})`)
-        .where('matricula.tipo <= 3')
+        .where('matricula.tipo<=3')
         .group('matricula.ano_censo');
 
-    const enrollmentsPerAdmDepQry = squel.select()
+    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('matricula')
-        .join('dependencia_adm', null, 'matricula.dependencia_adm_id=dependencia_adm.id')
+        .from('dependencia_adm')
+        .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 = squel.select()
+    const enrollmentsPerSchoolLevelQry = req.sql.clone()
         .field('\'Brasil\'', 'name')
-        .field('COALESCE(SUM(matricula.id), 0)', 'total')
+        .field('COALESCE(COUNT(matricula.id), 0)', 'total')
         .field('matricula.ano_censo', 'census_year')
         .field('etapa_ensino.desc_etapa', 'school_level_name')
         .from('matricula')
-        .join('etapa_ensino', null, 'matricula.etapa_ensino_id=etapa_ensino.id')
+        .from('etapa_ensino')
+        .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')
-        .order('etapa_ensino.desc_etapa');
+        .group('etapa_ensino.id')
+        .order('etapa_ensino.id');
 
     const queryLabels = [ "school", "school_per_location", "enrollment", "enrollment_per_adm_dep",
         "enrollment_per_school_level" ];
@@ -337,13 +334,6 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
     // 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}`);
@@ -351,23 +341,26 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
     });
 }, response('spatial'));
 
-spatialApp.get('/educational/school_level', rqf.parse(), rqf.build(), (req, res, next) => {
+spatialApp.get('/educational/school_level', (req, res, next) => {
     const enrollmentsPerSchoolLevelYearQry = squel.select()
         .field('MAX(matricula.ano_censo)', 'census_year')
         .from('matricula');
 
-    const enrollmentsPerSchoolLevelQry = req.sql.clone()
+    const enrollmentsPerSchoolLevelQry = squel.select()
         .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_name')
+        .field('etapa_ensino.desc_etapa', 'school_level')
         .from('matricula')
-        .join('etapa_ensino', null, 'matricula.etapa_ensino_id=etapa_ensino.id')
+        .from('etapa_ensino')
         .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');
 
-- 
GitLab


From 291103800e66bcc85c23feff4c43067500ab39cd Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Thu, 23 Mar 2017 11:08:01 -0300
Subject: [PATCH 25/30] :green_heart: Fix SQL and tests for /spatial

---
 src/libs/routes/spatial.js | 10 ++++----
 src/test/spatial.js        | 47 +++++++++++++++++++-------------------
 2 files changed, 28 insertions(+), 29 deletions(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index 58473b0e..2cc6e839 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -303,8 +303,8 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('COALESCE(COUNT(matricula.id), 0)', 'total')
         .field('matricula.ano_censo', 'census_year')
         .field('dependencia_adm.nome', 'adm_dependency_name')
-        .from('matricula')
         .from('dependencia_adm')
+        .from('matricula')
         .where('matricula.dependencia_adm_id=dependencia_adm.id')
         .where('matricula.tipo <= 3')
         .where(`matricula.ano_censo IN (${schoolClassYearQry})`)
@@ -317,8 +317,8 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('COALESCE(COUNT(matricula.id), 0)', 'total')
         .field('matricula.ano_censo', 'census_year')
         .field('etapa_ensino.desc_etapa', 'school_level_name')
-        .from('matricula')
         .from('etapa_ensino')
+        .from('matricula')
         .where('matricula.etapa_ensino_id=etapa_ensino.id')
         .where('matricula.tipo <= 3')
         .where(`matricula.ano_censo IN (${schoolClassYearQry})`)
@@ -341,18 +341,18 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
     });
 }, response('spatial'));
 
-spatialApp.get('/educational/school_level', (req, res, next) => {
+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 = squel.select()
+    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('matricula')
         .from('etapa_ensino')
+        .from('matricula')
         .where(`matricula.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`)
         .where('matricula.etapa_ensino_id = etapa_ensino.id')
         .where('matricula.tipo <= 3')
diff --git a/src/test/spatial.js b/src/test/spatial.js
index b0e53083..b4422e79 100644
--- a/src/test/spatial.js
+++ b/src/test/spatial.js
@@ -24,7 +24,7 @@ const server = require(`${libs}/app`);
 
 chai.use(chaiHttp);
 
-const testTimeout = 5000;
+const testTimeout = 60000;
 
 describe('test spatial', () => {
     it('should return the expected response format for sociodemographic data for the whole country', (done) => {
@@ -209,11 +209,10 @@ describe('test spatial', () => {
                 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 spatial', () => {
                 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 spatial', () => {
                     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 spatial', () => {
                     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 spatial', () => {
 
     it('should return the expected response format for educational data for a country region', (done) => {
         chai.request(server)
-            .get('/api/v1/spatial/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 spatial', () => {
                 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 spatial', () => {
                 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 spatial', () => {
                     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 spatial', () => {
                     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 spatial', () => {
 
     it('should return the expected response format for educational data for a country state', (done) => {
         chai.request(server)
-            .get('/api/v1/spatial/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 spatial', () => {
                 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 spatial', () => {
                 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 spatial', () => {
                     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 spatial', () => {
                     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 spatial', () => {
 
     it('should return the expected response format for educational data for a country city', (done) => {
         chai.request(server)
-            .get('/api/v1/spatial/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 spatial', () => {
                 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 spatial', () => {
                 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 spatial', () => {
                     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 spatial', () => {
                     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();
             });
@@ -478,7 +477,7 @@ describe('test spatial', () => {
 
     it('should return the correct format of enrollments per school level for a region', (done) => {
         chai.request(server)
-            .get('/api/v1/spatial/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 spatial', () => {
 
     it('should return the correct format of enrollments per school level for a state', (done) => {
         chai.request(server)
-            .get('/api/v1/spatial/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 spatial', () => {
 
     it('should return the correct format of enrollments per school level for a city', (done) => {
         chai.request(server)
-            .get('/api/v1/spatial/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
-- 
GitLab


From 82d03f7198cc709457f66c01745139849942e879 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Thu, 23 Mar 2017 12:06:27 -0300
Subject: [PATCH 26/30] :white_check_mark: Add tests to /enrollment

---
 gulpfile.babel.js      |  2 +-
 src/test/enrollment.js | 60 ++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/gulpfile.babel.js b/gulpfile.babel.js
index a6eb1a87..11e9416f 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: 30000}))
+    .pipe(mocha({timeout: 60000}))
     .pipe(istanbul.writeReports())
     .pipe(istanbul.enforceThresholds({
         thresholds: {
diff --git a/src/test/enrollment.js b/src/test/enrollment.js
index 3c19f29e..48bebc5f 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:2015,max_year:2015,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;
-- 
GitLab


From e7e9edf55a3276becc11f386ab081faba2bf40cb Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Fri, 24 Mar 2017 10:44:29 -0300
Subject: [PATCH 27/30] Add school name to /school response

---
 src/libs/routes/school.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/libs/routes/school.js b/src/libs/routes/school.js
index 1d44d771..d6e5f7d0 100644
--- a/src/libs/routes/school.js
+++ b/src/libs/routes/school.js
@@ -84,6 +84,7 @@ schoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.sql.from('escola')
         .field('escola.id')
         .field('escola.ano_censo', 'year')
+        .field('escola.nome_escola', 'name')
         .field('escola.estado_id', 'state_id')
         .field('escola.municipio_id', 'city_id');
     next();
-- 
GitLab


From d98371615eb6db6abc93fdf6e3c2519c0ebe94b0 Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Fri, 24 Mar 2017 11:10:40 -0300
Subject: [PATCH 28/30] Remove unused log in query_exec

---
 src/libs/db/query_exec.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/db/query_exec.js b/src/libs/db/query_exec.js
index 1a2f7387..c43b1dbe 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);
-- 
GitLab


From d27356171c6f1d197a39cef82a313398a1bd45aa Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Fri, 24 Mar 2017 11:11:11 -0300
Subject: [PATCH 29/30] Add schools_per_adm_dependency and
 enrollment_per_location at /spatial/educational

---
 src/libs/routes/spatial.js | 47 ++++++++++++++++++++++++++++++--------
 1 file changed, 38 insertions(+), 9 deletions(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index 2cc6e839..4c6489c1 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -260,8 +260,8 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('\'Total\'', 'location_name')
         .field('COUNT(DISTINCT(escola.id))', 'total')
         .field('escola.ano_censo', 'census_year')
-        .from('escola')
         .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')
@@ -272,10 +272,10 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('COUNT(DISTINCT(escola.id))', 'total')
         .field('escola.ano_censo', 'census_year')
         .field('localizacao.descricao', 'location_name')
-        .from('escola')
         .from('localizacao')
-        .where('escola.cod_localizacao=localizacao.id')
         .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')
@@ -284,9 +284,23 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
         .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(turma.ano_censo)')
-        .from('turma')
+        .field('MAX(matricula.ano_censo)')
+        .from('matricula')
         .toString();
 
     const enrollmentsQry = req.sql.clone()
@@ -327,10 +341,25 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
         .group('etapa_ensino.id')
         .order('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];
+    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 queryLabels = [ "school", "school_per_location", "school_per_adm_dependency", "enrollment", "enrollment_per_adm_dep",
+        "enrollment_per_school_level", "enrollment_per_location" ];
+    const querySet = [ totalSchoolsQry, schoolsPerLocationQry, schoolsPerAdmDependencyQry, enrollmentsQry,
+        enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry, enrollmentsPerLocationQry];
     // wait until all queries finish or one of them fail
     Promise.all(dbExecAll(querySet)).then((queryResults) => {
         req.result = processResultSet(queryResults, queryLabels);
-- 
GitLab


From 078cad7f08f3b510fc11846b1f6118c2bf14447f Mon Sep 17 00:00:00 2001
From: Vytor Calixto <vytorcalixto@gmail.com>
Date: Fri, 24 Mar 2017 11:24:10 -0300
Subject: [PATCH 30/30] Add enrollment per school level and adm dependency in
 /spatial/educational

---
 src/libs/routes/spatial.js | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/src/libs/routes/spatial.js b/src/libs/routes/spatial.js
index 4c6489c1..2196c7a2 100644
--- a/src/libs/routes/spatial.js
+++ b/src/libs/routes/spatial.js
@@ -356,10 +356,30 @@ spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
         .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", "enrollment_per_location", "enrollment_per_school_level_adm_dependency" ];
     const querySet = [ totalSchoolsQry, schoolsPerLocationQry, schoolsPerAdmDependencyQry, enrollmentsQry,
-        enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry, enrollmentsPerLocationQry];
+        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);
-- 
GitLab