From 7e4819e92e4e824025b8ee6a33da3a22e75285fc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Tue, 2 Aug 2016 10:06:54 -0300
Subject: [PATCH 01/58] Refactor enrollments route to use function chaining

---
 libs/routes/api.js | 117 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 92 insertions(+), 25 deletions(-)

diff --git a/libs/routes/api.js b/libs/routes/api.js
index aa13166f..9bea796c 100644
--- a/libs/routes/api.js
+++ b/libs/routes/api.js
@@ -1,3 +1,5 @@
+'use strict';
+
 var express = require('express')
 var xml = require('js2xmlparser')
 var router = express.Router()
@@ -56,39 +58,34 @@ router.get('/data', function(req, res) {
     })
 })
 
-router.get('/enrollments', function(req, res) {
-    var params = req.query;
-    var id = 0;
-    var location_id = 0;
-    var adm_dependency_id = 0;
-    var census_year = 0;
-    var enrollmentSql = "";
+router.get('/enrollments', function(req, res, next) {
+    const params = req.query;
 
-    if (params.id)
-    {
-        id = parseInt(params.id, 10);
+    if (params.id) {
+        req.id = parseInt(params.id, 10);
     }
 
-    if (params.location_id)
-    {
-        location_id = parseInt(params.location_id, 10);
+    if (params.location_id) {
+        req.location_id = parseInt(params.location_id, 10);
     }
 
-    if (params.adm_dependency_id)
-    {
-        adm_dependency_id = parseInt(params.adm_dependency_id, 10);
+    if (params.adm_dependency_id) {
+        req.adm_dependency_id = parseInt(params.adm_dependency_id, 10);
     }
 
-    if (params.census_year)
-    {
-        census_year = parseInt(params.census_year, 10);
+    if (params.census_year) {
+        req.census_year = parseInt(params.census_year, 10);
     }
 
-    /**
-     * FIXME: parameter substitution in the queries is not safe (vulnerable to
-     * SQL injection). Substitution from MonetDB module is not working for some
-     * reason.
-     */
+    if (params.aggregate) {
+        log.debug('aggregate parameter detected');
+        next('route');
+    } else {
+        log.debug('No aggregate parameter detected');
+        next();
+    }
+
+    /*
     switch(params.aggregate)
     {
     case "city":
@@ -132,7 +129,77 @@ router.get('/enrollments', function(req, res) {
           });
         }
         log.debug("All resources were released");
+    }, function(error) {
+
     });
-})
+   */
+}, function(req, res, next) {
+    /** When no +aggregate+ parameter value is specified on the request, then
+     * assign the query to compute the result for the whole country.
+     */
+    log.debug('Using SQL query for the whole country');
+    req.sql_query = 'SELECT * FROM turmas LIMIT 1';
+    next('route');
+});
+
+router.get('/enrollments', function(req, res, next) {
+    const params = req.query;
+    if (!params.aggregate) {
+        next('route');
+    } else if (params.aggregate == 'region') {
+        log.debug('Using enrollments query for regions');
+        req.sql_query = 'SELECT * FROM turmas LIMIT 1';
+    }
+    next('route');
+});
+
+router.get('/enrollments', function(req, res, next) {
+    const params = req.query;
+    if (!params.aggregate) {
+        next('route');
+    } else if (params.aggregate == 'state') {
+        log.debug('Using enrollments query for states');
+        req.sql_query = 'SELECT * FROM turmas LIMIT 1';
+    }
+    next('route');
+});
+
+router.get('/enrollments', function(req, res, next) {
+    const params = req.query;
+    if (!params.aggregate) {
+        next('route');
+    } else if (params.aggregate == 'city') {
+        log.debug('Using enrollments query for cities');
+        req.sql_query = 'SELECT * FROM turmas LIMIT 1';
+    }
+    next('route');
+});
+
+router.get('/enrollments', function(req, res, next) {
+    log.debug('Request parameters: ${req}?');
+    if (!req.sql_query) {
+        /* Should only happen if there is a bug in the chaining of the
+         * '/enrollments' route, since when no +aggregate+ parameter is given,
+         * it defaults to use the query for the whole country.
+         */
+        log.error('BUG -- No SQL query was found to be executed!');
+        res.status(501).end();
+    } else {
+        log.debug('SQL query: ${req.sql_query}?');
+        conn.query(req.sql_query, true).then(function(result) {
+            log.debug(result);
+            if (req.query.format === 'csv') {
+                res.csv(result.data);
+            } else if (req.query.format === 'xml') {
+                res.send(xml('result', JSON.stringify({enrollments: result.data})));
+            } else {
+                res.json({ result: result.data });
+            }
+        }, function(error) {
+            log.error('SQL query error: ${error}?');
+            res.status(501).end();
+        });
+    }
+});
 
 module.exports = router
-- 
GitLab


From 1baf5471466e41d3239dae94fe10d17450e4eb8a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 3 Aug 2016 13:46:24 -0300
Subject: [PATCH 02/58] Refactor enrollments route to include query building

Changes:
- Add error handling in the database query function. When a database error
occurs, it is logged in the server and a plain text response is returned to the
user.
- Implement the building of queries inside the API, which removes the
  need to have stored functions/procedures on the database, which is not
  flexible or efficient to maintain/extend.
- Use route chaining to determine which function will build the query
  that needs to be executed. The last function is the one that actually
  sends the query to the database and send the response.

Future Work:
- Instead of returning a plain text response, return a HTTP 501 status,
  which is currently not possible. When one attempts to send such status
  the framework throws an error that the headers were already sent for
  the current request.
- Chaining based on the route might not be the best solution here.
---
 libs/routes/api.js | 262 +++++++++++++++++++++++++++++----------------
 1 file changed, 168 insertions(+), 94 deletions(-)

diff --git a/libs/routes/api.js b/libs/routes/api.js
index 9bea796c..16105a85 100644
--- a/libs/routes/api.js
+++ b/libs/routes/api.js
@@ -60,144 +60,218 @@ router.get('/data', function(req, res) {
 
 router.get('/enrollments', function(req, res, next) {
     const params = req.query;
+    req.paramCnt = 0;
 
-    if (params.id) {
+    if (typeof params.id !== 'undefined') {
         req.id = parseInt(params.id, 10);
+        req.paramCnt += 1;
     }
 
-    if (params.location_id) {
+    if (typeof params.location_id !== 'undefined') {
         req.location_id = parseInt(params.location_id, 10);
+        req.paramCnt += 1;
     }
 
-    if (params.adm_dependency_id) {
+    if (typeof params.adm_dependency_id !== 'undefined') {
         req.adm_dependency_id = parseInt(params.adm_dependency_id, 10);
+        req.paramCnt += 1;
     }
 
-    if (params.census_year) {
+    if (typeof params.census_year !== 'undefined') {
         req.census_year = parseInt(params.census_year, 10);
+        req.paramCnt += 1;
     }
 
-    if (params.aggregate) {
-        log.debug('aggregate parameter detected');
-        next('route');
-    } else {
-        log.debug('No aggregate parameter detected');
-        next();
-    }
+    next();
+});
 
-    /*
-    switch(params.aggregate)
-    {
-    case "city":
-        if (id) {
-            enrollmentSql = "SELECT nome AS name, total FROM mat_municipio(" + id + "," + census_year + "," + adm_dependency_id + "," + location_id + ")";
-        } else {
-            enrollmentSql = "SELECT nome AS name, total FROM mat_municipios(" + census_year + "," + adm_dependency_id + "," + location_id + ")";
-        }
-        break;
-    case "state":
-        if (id) {
-            enrollmentSql = "SELECT nome AS name, total FROM mat_estado(" + id + "," + census_year + "," + adm_dependency_id + "," + location_id + ")";
-        } else {
-            enrollmentSql = "SELECT nome AS name, total FROM mat_estados(" + census_year + "," + adm_dependency_id + "," + location_id + ")";
-        }
-        break;
-    case "region":
-        if (id) {
-            enrollmentSql = "SELECT nome AS name, total FROM mat_regiao(" + id + "," + census_year + "," + adm_dependency_id + "," + location_id + ")";
-        } else {
-            enrollmentSql = "SELECT nome AS name, total FROM mat_regioes(" + census_year + "," + adm_dependency_id + "," + location_id + ")";
-        }
-        break;
-    default:
-        enrollmentSql = "SELECT nome AS name, total FROM mat_brasil(" + census_year + "," + adm_dependency_id + "," + location_id + ")";
-    }
+router.get('/enrollments', function(req, res, next) {
+    const params = req.query;
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
+        log.debug('Using enrollments query for regions');
+        req.sqlQuery = 'SELECT r.nome, COALESCE(SUM(t.num_matriculas), 0) AS total '
+            + 'FROM regioes AS r '
+            + 'INNER JOIN estados AS e ON r.pk_regiao_id = e.fk_regiao_id '
+            + 'INNER JOIN municipios AS m ON e.pk_estado_id = m.fk_estado_id '
+            + 'LEFT OUTER JOIN turmas AS t ON ( '
+            + 'm.pk_municipio_id = t.fk_municipio_id ';
+        req.sqlQueryParams = [];
 
-    log.debug(params);
-    log.debug("Executing query: " + enrollmentSql);
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
 
-    conn.query(enrollmentSql, true).then(function(result) {
-        log.debug(result);
-        if (req.query.format === 'csv') {
-          res.csv(result.data);
-        } else if (req.query.format === 'xml') {
-          res.send(xml("result", JSON.stringify({enrollments: result.data})))
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
         }
-        else {
-          res.json({
-              result: result.data
-          });
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
         }
-        log.debug("All resources were released");
-    }, function(error) {
 
-    });
-   */
-}, function(req, res, next) {
-    /** When no +aggregate+ parameter value is specified on the request, then
-     * assign the query to compute the result for the whole country.
-     */
-    log.debug('Using SQL query for the whole country');
-    req.sql_query = 'SELECT * FROM turmas LIMIT 1';
-    next('route');
+        req.sqlQuery += ')';
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'r.pk_regiao_id = ?';
+            req.sqlQueryParams.push(req.id);
+        }
+        req.sqlQuery += ' GROUP BY r.nome';
+    }
+    next();
 });
 
 router.get('/enrollments', function(req, res, next) {
     const params = req.query;
-    if (!params.aggregate) {
-        next('route');
-    } else if (params.aggregate == 'region') {
-        log.debug('Using enrollments query for regions');
-        req.sql_query = 'SELECT * FROM turmas LIMIT 1';
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
+        log.debug('Using enrollments query for states');
+        req.sqlQuery = 'SELECT e.nome, COALESCE(SUM(t.num_matriculas), 0) as total '
+            + 'FROM estados AS e '
+            + 'INNER JOIN municipios AS m ON m.fk_estado_id = e.pk_estado_id '
+            + 'LEFT OUTER JOIN turmas AS t ON ('
+            + 'm.pk_municipio_id = t.fk_municipio_id ';
+        req.sqlQueryParams = [];
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        req.sqlQuery += ')';
+
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += " WHERE ";
+            req.sqlQuery += "e.pk_estado_id = ?";
+            req.sqlQueryParams.push(req.id);
+        }
+
+        req.sqlQuery += ' GROUP BY e.nome';
     }
-    next('route');
+    next();
 });
 
 router.get('/enrollments', function(req, res, next) {
     const params = req.query;
-    if (!params.aggregate) {
-        next('route');
-    } else if (params.aggregate == 'state') {
-        log.debug('Using enrollments query for states');
-        req.sql_query = 'SELECT * FROM turmas LIMIT 1';
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
+        log.debug('Using enrollments query for cities');
+        req.sqlQuery = 'SELECT m.nome, COALESCE(SUM(t.num_matriculas), 0) as total '
+            + 'FROM municipios AS m '
+            + 'LEFT OUTER JOIN turmas AS t ON ( '
+            + 'm.pk_municipio_id = t.fk_municipio_id';
+        req.sqlQueryParams = [];
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        req.sqlQuery += ')';
+
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += " WHERE ";
+            req.sqlQuery += "m.pk_municipio_id = ?";
+            req.sqlQueryParams.push(req.id);
+        }
+
+        req.sqlQuery += 'GROUP BY m.nome';
     }
-    next('route');
+    next();
 });
 
 router.get('/enrollments', function(req, res, next) {
     const params = req.query;
-    if (!params.aggregate) {
-        next('route');
-    } else if (params.aggregate == 'city') {
-        log.debug('Using enrollments query for cities');
-        req.sql_query = 'SELECT * FROM turmas LIMIT 1';
+    if (typeof params.aggregate === 'undefined') {
+        log.debug('Using enrollments query for the whole country');
+        req.sqlQuery = 'SELECT \'Brasil\', COALESCE(SUM(t.num_matriculas),0) AS total '
+            + 'FROM turmas AS t';
+        req.sqlQueryParams = [];
+
+        if (req.paramCnt > 0) {
+            req.sqlQuery += ' WHERE ';
+        }
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.fk_dependencia_adm = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
     }
-    next('route');
+    next();
 });
 
 router.get('/enrollments', function(req, res, next) {
-    log.debug('Request parameters: ${req}?');
-    if (!req.sql_query) {
+    log.debug('Request parameters: ${ req }?');
+    if (!req.sqlQuery) {
         /* Should only happen if there is a bug in the chaining of the
          * '/enrollments' route, since when no +aggregate+ parameter is given,
          * it defaults to use the query for the whole country.
          */
         log.error('BUG -- No SQL query was found to be executed!');
-        res.status(501).end();
+        res.send('Request could not be satisfied due to an internal error');
     } else {
-        log.debug('SQL query: ${req.sql_query}?');
-        conn.query(req.sql_query, true).then(function(result) {
-            log.debug(result);
-            if (req.query.format === 'csv') {
-                res.csv(result.data);
-            } else if (req.query.format === 'xml') {
-                res.send(xml('result', JSON.stringify({enrollments: result.data})));
-            } else {
-                res.json({ result: result.data });
-            }
+        log.debug('SQL query: ${ req.sqlQuery }?');
+        log.debug(req.sqlQuery);
+        conn.prepare(req.sqlQuery, true).then(function(dbQuery) {
+            dbQuery.exec(req.sqlQueryParams).then(function(dbResult) {
+                log.debug(dbResult);
+                if (req.query.format === 'csv') {
+                    res.csv(dbResult.data);
+                } else if (req.query.format === 'xml') {
+                    res.send(xml('result', JSON.stringify({enrollments: dbResult.data})));
+                } else {
+                    res.json({ result: dbResult.data });
+                }
+            });
         }, function(error) {
-            log.error('SQL query error: ${error}?');
-            res.status(501).end();
+                log.error('SQL query error: ${ error.message }?');
+                log.debug(error);
+                res.send('Request could not be satisfied due to an internal error');
         });
     }
 });
-- 
GitLab


From 4944e77e58b30fa6868c197acc4410257147a377 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 3 Aug 2016 14:17:47 -0300
Subject: [PATCH 03/58] Fix enrollments route response to comply with tests

---
 libs/routes/api.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/libs/routes/api.js b/libs/routes/api.js
index 16105a85..b5492602 100644
--- a/libs/routes/api.js
+++ b/libs/routes/api.js
@@ -89,7 +89,7 @@ router.get('/enrollments', function(req, res, next) {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
         log.debug('Using enrollments query for regions');
-        req.sqlQuery = 'SELECT r.nome, COALESCE(SUM(t.num_matriculas), 0) AS total '
+        req.sqlQuery = 'SELECT r.nome AS name, COALESCE(SUM(t.num_matriculas), 0) AS total '
             + 'FROM regioes AS r '
             + 'INNER JOIN estados AS e ON r.pk_regiao_id = e.fk_regiao_id '
             + 'INNER JOIN municipios AS m ON e.pk_estado_id = m.fk_estado_id '
@@ -130,7 +130,7 @@ router.get('/enrollments', function(req, res, next) {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
         log.debug('Using enrollments query for states');
-        req.sqlQuery = 'SELECT e.nome, COALESCE(SUM(t.num_matriculas), 0) as total '
+        req.sqlQuery = 'SELECT e.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total '
             + 'FROM estados AS e '
             + 'INNER JOIN municipios AS m ON m.fk_estado_id = e.pk_estado_id '
             + 'LEFT OUTER JOIN turmas AS t ON ('
@@ -172,7 +172,7 @@ router.get('/enrollments', function(req, res, next) {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
         log.debug('Using enrollments query for cities');
-        req.sqlQuery = 'SELECT m.nome, COALESCE(SUM(t.num_matriculas), 0) as total '
+        req.sqlQuery = 'SELECT m.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total '
             + 'FROM municipios AS m '
             + 'LEFT OUTER JOIN turmas AS t ON ( '
             + 'm.pk_municipio_id = t.fk_municipio_id';
@@ -213,7 +213,7 @@ router.get('/enrollments', function(req, res, next) {
     const params = req.query;
     if (typeof params.aggregate === 'undefined') {
         log.debug('Using enrollments query for the whole country');
-        req.sqlQuery = 'SELECT \'Brasil\', COALESCE(SUM(t.num_matriculas),0) AS total '
+        req.sqlQuery = 'SELECT \'Brasil\' AS name, COALESCE(SUM(t.num_matriculas),0) AS total '
             + 'FROM turmas AS t';
         req.sqlQueryParams = [];
 
-- 
GitLab


From 2c75a3db404eec39b5fe7bb7e506153109d1b43b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 3 Aug 2016 14:25:05 -0300
Subject: [PATCH 04/58] Change type verification to typeof

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

diff --git a/libs/routes/api.js b/libs/routes/api.js
index b5492602..feebd9c2 100644
--- a/libs/routes/api.js
+++ b/libs/routes/api.js
@@ -247,7 +247,7 @@ router.get('/enrollments', function(req, res, next) {
 
 router.get('/enrollments', function(req, res, next) {
     log.debug('Request parameters: ${ req }?');
-    if (!req.sqlQuery) {
+    if (typeof req.sqlQuery === 'undefined') {
         /* Should only happen if there is a bug in the chaining of the
          * '/enrollments' route, since when no +aggregate+ parameter is given,
          * it defaults to use the query for the whole country.
-- 
GitLab


From 8d77d0145a3c604ea131c2e833b0e6aec7e004d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 10 Aug 2016 14:39:07 -0300
Subject: [PATCH 05/58] Add education level and fix issue with
 adm_dependency_id

Changelog:
- Add education level to all queries

- Add route '/education_level' to query the available education levels.
  The response is a set of pairs (id, education_level)

- Change var declarations to their respective const and let counterparts

- Fix wrong foreign key name in the aggregation by states. When a
  adm_dependency_id parameter was supplied, the query would fail.
---
 libs/routes/api.js | 107 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 82 insertions(+), 25 deletions(-)

diff --git a/libs/routes/api.js b/libs/routes/api.js
index feebd9c2..9911bf10 100644
--- a/libs/routes/api.js
+++ b/libs/routes/api.js
@@ -1,28 +1,28 @@
 'use strict';
 
-var express = require('express')
-var xml = require('js2xmlparser')
-var router = express.Router()
+const express = require('express');
+const xml = require('js2xmlparser');
+const enrollmentsApp = express();
 
-var libs = process.cwd() + '/libs/'
+const libs = process.cwd() + '/libs/';
 
-var log = require(libs + 'log')(module)
-var config = require(libs + 'config')
+const log = require(libs + 'log')(module);
+const config = require(libs + 'config');
 
-var conn = require(libs + 'db/monet')
+const conn = require(libs + 'db/monet');
 
-router.get('/', function (req, res) {
+enrollmentsApp.get('/', function (req, res) {
     res.json({
         msg: 'SimCAQ API is running'
-    })
-})
+    });
+});
 
 /**
  * Complete range of the enrollments dataset
  *
  * Returns a tuple of start and ending years of the complete enrollments dataset.
  */
-router.get('/year_range', function(req, res) {
+enrollmentsApp.get('/year_range', function(req, res) {
     var yearSql = "SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo) AS end_year FROM turmas AS t";
     conn.query(yearSql, true).then(function(result) {
       if (req.query.format === 'csv') {
@@ -35,16 +35,38 @@ router.get('/year_range', function(req, res) {
             result: result.data
         });
       }
+    }, function(error) {
+        log.error('SQL query error: ${ error.message }?');
+        log.debug(error);
+        res.send('Request could not be satisfied due to an internal error');
     });
-})
+});
+
+enrollmentsApp.get('/education_level', function(req, res) {
+    var edLevelId = "SELECT pk_etapa_ensino_id AS id, desc_etapa AS education_level FROM etapa_ensino";
+    conn.query(edLevelId, true).then(function(result) {
+      if (req.query.format === 'csv') {
+        res.csv(result.data);
+      } else if (req.query.format === 'xml') {
+        res.send(xml("result", JSON.stringify({year_range: result.data})))
+      }
+      else {
+        res.json({
+            result: result.data
+        });
+      }
+    }, function(error) {
+        log.error('SQL query error: ${ error.message }?');
+        log.debug(error);
+        res.send('Request could not be satisfied due to an internal error');
+    });
+});
 
-router.get('/data', function(req, res) {
+enrollmentsApp.get('/data', function(req, res) {
     log.debug(req.query)
     log.debug(req.query.met)
     log.debug(req.query.dim)
-    conn.query(
-        'SELECT * FROM turmas'
-    ).then(function(result) {
+    conn.query('SELECT * FROM turmas LIMIT 5', true).then(function(result) {
       if (req.query.format === 'csv') {
         res.csv(result.data);
       } else if (req.query.format === 'xml') {
@@ -55,10 +77,14 @@ router.get('/data', function(req, res) {
             result: result.data
         });
       }
-    })
+    }, function(error) {
+        log.error('SQL query error: ${ error.message }?');
+        log.debug(error);
+        res.send('Request could not be satisfied due to an internal error');
+    });
 })
 
-router.get('/enrollments', function(req, res, next) {
+enrollmentsApp.use('/enrollments', function(req, res, next) {
     const params = req.query;
     req.paramCnt = 0;
 
@@ -82,10 +108,15 @@ router.get('/enrollments', function(req, res, next) {
         req.paramCnt += 1;
     }
 
+    if (typeof params.education_level_id !== 'undefined') {
+        req.education_level_id = parseInt(params.education_level_id, 10);
+        req.paramCnt += 1;
+    }
+
     next();
 });
 
-router.get('/enrollments', function(req, res, next) {
+enrollmentsApp.use('/enrollments', function(req, res, next) {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
         log.debug('Using enrollments query for regions');
@@ -115,6 +146,12 @@ router.get('/enrollments', function(req, res, next) {
             req.sqlQueryParams.push(req.location_id);
         }
 
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
         req.sqlQuery += ')';
         if (typeof req.id !== 'undefined') {
             req.sqlQuery += ' WHERE ';
@@ -126,7 +163,7 @@ router.get('/enrollments', function(req, res, next) {
     next();
 });
 
-router.get('/enrollments', function(req, res, next) {
+enrollmentsApp.use('/enrollments', function(req, res, next) {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
         log.debug('Using enrollments query for states');
@@ -155,6 +192,12 @@ router.get('/enrollments', function(req, res, next) {
             req.sqlQueryParams.push(req.location_id);
         }
 
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
         req.sqlQuery += ')';
 
         if (typeof req.id !== 'undefined') {
@@ -168,7 +211,7 @@ router.get('/enrollments', function(req, res, next) {
     next();
 });
 
-router.get('/enrollments', function(req, res, next) {
+enrollmentsApp.use('/enrollments', function(req, res, next) {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
         log.debug('Using enrollments query for cities');
@@ -196,6 +239,12 @@ router.get('/enrollments', function(req, res, next) {
             req.sqlQueryParams.push(req.location_id);
         }
 
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
         req.sqlQuery += ')';
 
         if (typeof req.id !== 'undefined') {
@@ -209,7 +258,7 @@ router.get('/enrollments', function(req, res, next) {
     next();
 });
 
-router.get('/enrollments', function(req, res, next) {
+enrollmentsApp.use('/enrollments', function(req, res, next) {
     const params = req.query;
     if (typeof params.aggregate === 'undefined') {
         log.debug('Using enrollments query for the whole country');
@@ -230,7 +279,7 @@ router.get('/enrollments', function(req, res, next) {
             if (req.sqlQueryParams.length > 0) {
                 req.sqlQuery += ' AND ';
             }
-            req.sqlQuery += 't.fk_dependencia_adm = ?';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
             req.sqlQueryParams.push(req.adm_dependency_id);
         }
 
@@ -241,11 +290,19 @@ router.get('/enrollments', function(req, res, next) {
             req.sqlQuery += 't.id_localizacao = ?';
             req.sqlQueryParams.push(req.location_id);
         }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
     }
     next();
 });
 
-router.get('/enrollments', function(req, res, next) {
+enrollmentsApp.get('/enrollments', function(req, res, next) {
     log.debug('Request parameters: ${ req }?');
     if (typeof req.sqlQuery === 'undefined') {
         /* Should only happen if there is a bug in the chaining of the
@@ -276,4 +333,4 @@ router.get('/enrollments', function(req, res, next) {
     }
 });
 
-module.exports = router
+module.exports = enrollmentsApp
-- 
GitLab


From 00a87decca32d5333e8bfe0864e2734c9dd9f29d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 17 Aug 2016 19:50:19 -0300
Subject: [PATCH 06/58] Change API to use ECMAScript6
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Major modifications:
- Source code is now in the src directory
- All source files were adapted to ECMAScript6
- Add Gulpfile and Babel to transcompile the project from ES6 to ES5 (until Node.js fully supports ES6)
	- By running gulp one generates the build directory with the files transcompiled, from which the API can be run
- Add ESLint to check for syntax errors and enforce code standards
	- Javascript coding standard currently adopted is AirBnB with 4 spaces
- Replace monetdb package with monetdb-pool, which allows for concurrent queries over a pool of connections while maintaining the same interface

TODO (in order of priority, from high to low):
- Add Gulp tasks to handle automatic building, tests and running the server in order to deprecate using npm
- Implement decorator to execute the SQL queries and reduce code duplication.
- Implement SQL query builder (e.g. squel.js) to erradicate the need for embedding SQL directly into the code (which is error-prone).
- Change enrollments route not to use route chaining in order to decide which SQL query is appropriate to respond the user's request.
- Implement decorator for API responses and also reduce code duplication.
- Split up tests into and add more test cases.

Signed-off-by: João Victor Risso <jvtr12@c3sl.ufpr.br>
---
 .babelrc                         |   1 +
 .eslintrc.json                   |  13 ++
 .gitignore                       |   1 -
 .gitlab-ci.yml                   |   2 +-
 build/config.json                |  16 ++
 build/libs/app.js                |  43 +++++
 build/libs/config.js             |   7 +
 build/libs/db/monet.js           |  24 +++
 build/libs/db/query_decorator.js |  22 +++
 build/libs/enrollment/all.js     |   1 +
 build/libs/enrollment/city.js    |   1 +
 build/libs/enrollment/common.js  |  15 ++
 build/libs/enrollment/country.js |   1 +
 build/libs/enrollment/region.js  |   1 +
 build/libs/enrollment/state.js   |   1 +
 build/libs/log.js                |  33 ++++
 build/libs/query_decorator.js    |  22 +++
 build/libs/routes/api.js         | 308 +++++++++++++++++++++++++++++++
 build/libs/routes/cities.js      |  85 +++++++++
 build/libs/routes/regions.js     |  54 ++++++
 build/libs/routes/states.js      |  70 +++++++
 build/logs/.gitignore            |   1 +
 build/server.js                  |  18 ++
 build/test/test.js               | 139 ++++++++++++++
 config.json                      |   2 +-
 gulpfile.babel.js                |  15 ++
 libs/app.js                      |  53 ------
 libs/config.js                   |   9 -
 libs/db/monet.js                 |  19 --
 libs/log.js                      |  35 ----
 libs/routes/cities.js            |  66 -------
 libs/routes/regions.js           |  45 -----
 libs/routes/states.js            |  55 ------
 package.json                     |  13 +-
 server.js                        |  13 --
 src/libs/app.js                  |  41 ++++
 src/libs/config.js               |   7 +
 src/libs/db/monet.js             |  22 +++
 src/libs/db/query_decorator.js   |  25 +++
 src/libs/enrollment/all.js       |   0
 src/libs/enrollment/city.js      |   0
 src/libs/enrollment/common.js    |  14 ++
 src/libs/enrollment/country.js   |   0
 src/libs/enrollment/region.js    |   0
 src/libs/enrollment/state.js     |   0
 src/libs/log.js                  |  34 ++++
 {libs => src/libs}/routes/api.js | 193 ++++++++++---------
 src/libs/routes/cities.js        |  95 ++++++++++
 src/libs/routes/regions.js       |  58 ++++++
 src/libs/routes/states.js        |  77 ++++++++
 src/server.js                    |  16 ++
 src/test/test.js                 | 155 ++++++++++++++++
 test/test.js                     | 158 ----------------
 53 files changed, 1544 insertions(+), 555 deletions(-)
 create mode 100644 .babelrc
 create mode 100644 .eslintrc.json
 create mode 100644 build/config.json
 create mode 100644 build/libs/app.js
 create mode 100644 build/libs/config.js
 create mode 100644 build/libs/db/monet.js
 create mode 100644 build/libs/db/query_decorator.js
 create mode 100644 build/libs/enrollment/all.js
 create mode 100644 build/libs/enrollment/city.js
 create mode 100644 build/libs/enrollment/common.js
 create mode 100644 build/libs/enrollment/country.js
 create mode 100644 build/libs/enrollment/region.js
 create mode 100644 build/libs/enrollment/state.js
 create mode 100644 build/libs/log.js
 create mode 100644 build/libs/query_decorator.js
 create mode 100644 build/libs/routes/api.js
 create mode 100644 build/libs/routes/cities.js
 create mode 100644 build/libs/routes/regions.js
 create mode 100644 build/libs/routes/states.js
 create mode 100644 build/logs/.gitignore
 create mode 100644 build/server.js
 create mode 100644 build/test/test.js
 create mode 100644 gulpfile.babel.js
 delete mode 100644 libs/app.js
 delete mode 100644 libs/config.js
 delete mode 100644 libs/db/monet.js
 delete mode 100644 libs/log.js
 delete mode 100644 libs/routes/cities.js
 delete mode 100644 libs/routes/regions.js
 delete mode 100644 libs/routes/states.js
 delete mode 100644 server.js
 create mode 100644 src/libs/app.js
 create mode 100644 src/libs/config.js
 create mode 100644 src/libs/db/monet.js
 create mode 100644 src/libs/db/query_decorator.js
 create mode 100644 src/libs/enrollment/all.js
 create mode 100644 src/libs/enrollment/city.js
 create mode 100644 src/libs/enrollment/common.js
 create mode 100644 src/libs/enrollment/country.js
 create mode 100644 src/libs/enrollment/region.js
 create mode 100644 src/libs/enrollment/state.js
 create mode 100644 src/libs/log.js
 rename {libs => src/libs}/routes/api.js (65%)
 create mode 100644 src/libs/routes/cities.js
 create mode 100644 src/libs/routes/regions.js
 create mode 100644 src/libs/routes/states.js
 create mode 100644 src/server.js
 create mode 100644 src/test/test.js
 delete mode 100644 test/test.js

diff --git a/.babelrc b/.babelrc
new file mode 100644
index 00000000..834a48a4
--- /dev/null
+++ b/.babelrc
@@ -0,0 +1 @@
+{ 'presets': ['es2015'] }
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 00000000..52b136d7
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,13 @@
+{
+    "extends": "airbnb",
+    "plugins": [
+        "react",
+        "jsx-a11y",
+        "import"
+    ],
+    "rules": {
+        "indent": [ "error", 4 ],
+        "no-unused-vars": [ "error", { "args": "none" }],
+		"no-param-reassign": [ "off" ]
+    }
+}
diff --git a/.gitignore b/.gitignore
index 1be0326a..f4e4627c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,7 +10,6 @@ lib-cov
 *.gz
 
 pids
-logs
 results
 
 npm-debug.log
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3171c9f7..a3c44c8f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,6 +7,6 @@ before_script:
 run_tests:
   stage: test
   script:
-    - npm test
+    - gulp && cd build/ && mocha
   tags:
     - node
diff --git a/build/config.json b/build/config.json
new file mode 100644
index 00000000..c919cf1c
--- /dev/null
+++ b/build/config.json
@@ -0,0 +1,16 @@
+{
+    "port": 3000,
+    "monetdb": {
+        "host": "localhost",
+        "port": 50000,
+        "dbname": "simcaq_dev",
+        "user": "monetdb",
+        "password":"monetdb",
+        "nrConnections": 16
+    },
+    "default": {
+        "api": {
+            "version" : "v1"
+        }
+    }
+}
diff --git a/build/libs/app.js b/build/libs/app.js
new file mode 100644
index 00000000..a678b1c4
--- /dev/null
+++ b/build/libs/app.js
@@ -0,0 +1,43 @@
+'use strict';
+
+var express = require('express');
+var cookieParser = require('cookie-parser');
+var bodyParser = require('body-parser');
+var methodOverride = require('method-override');
+var cors = require('cors');
+
+var log = require('./log')(module);
+
+var api = require('./routes/api');
+var states = require('./routes/states');
+var regions = require('./routes/regions');
+var cities = require('./routes/cities');
+
+var app = express();
+
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({ extended: false }));
+app.use(cookieParser());
+app.use(cors());
+app.use(methodOverride());
+
+app.use('/v1/', api);
+app.use('/v1/states', states);
+app.use('/v1/regions', regions);
+app.use('/v1/cities', cities);
+
+// catch 404 and forward to error handler
+app.use(function (req, res, next) {
+    res.status(404);
+    log.debug('%s %d %s', req.method, res.statusCode, req.url);
+    res.json({ error: 'Not found' }).end();
+});
+
+// error handlers
+app.use(function (err, req, res, next) {
+    res.status(err.status || 500);
+    log.error('%s %d %s', req.method, res.statusCode, err.message);
+    res.json({ error: err.message }).end();
+});
+
+module.exports = app;
\ No newline at end of file
diff --git a/build/libs/config.js b/build/libs/config.js
new file mode 100644
index 00000000..2f7bd18e
--- /dev/null
+++ b/build/libs/config.js
@@ -0,0 +1,7 @@
+'use strict';
+
+var nconf = require('nconf');
+
+nconf.argv().env().file({ file: process.cwd() + '/config.json' });
+
+module.exports = nconf;
\ No newline at end of file
diff --git a/build/libs/db/monet.js b/build/libs/db/monet.js
new file mode 100644
index 00000000..c8d29099
--- /dev/null
+++ b/build/libs/db/monet.js
@@ -0,0 +1,24 @@
+'use strict';
+
+var MonetDBPool = require('monetdb-pool');
+
+var libs = process.cwd() + '/libs';
+
+var config = require(libs + '/config');
+
+var poolOptions = {
+    nrConnections: config.get('monetdb:nrConnections')
+};
+
+var options = {
+    host: config.get('monetdb:host'),
+    port: config.get('monetdb:port'),
+    dbname: config.get('monetdb:dbname'),
+    user: config.get('monetdb:user'),
+    password: config.get('monetdb:password')
+};
+
+var conn = new MonetDBPool(poolOptions, options);
+conn.connect();
+
+module.exports = conn;
\ No newline at end of file
diff --git a/build/libs/db/query_decorator.js b/build/libs/db/query_decorator.js
new file mode 100644
index 00000000..ce0697d7
--- /dev/null
+++ b/build/libs/db/query_decorator.js
@@ -0,0 +1,22 @@
+"use strict";
+
+var libs = process.cwd() + "/libs";
+var log = require(libs + "/log")(module);
+var conn = require(libs + "/db/monet");
+
+/**
+ * Basic decorator to wrap SQL query strings
+ */
+exports.execQuery = function (sqlQuery, sqlQueryParams) {
+    log.debug("Executing SQL query '" + sqlQuery + "' with params '" + sqlQueryParams + "'");
+    conn.prepare(sqlQuery, true).then(function (dbQuery) {
+        dbQuery.exec(sqlQueryParams).then(function (dbResult) {
+            log.debug(dbResult.data);
+            log.debug("Query result: " + dbResult.data);
+            return dbResult;
+        }, function (error) {
+            log.error("SQL query execution error: " + error.message);
+            return error;
+        });
+    });
+};
\ No newline at end of file
diff --git a/build/libs/enrollment/all.js b/build/libs/enrollment/all.js
new file mode 100644
index 00000000..9a390c31
--- /dev/null
+++ b/build/libs/enrollment/all.js
@@ -0,0 +1 @@
+"use strict";
\ No newline at end of file
diff --git a/build/libs/enrollment/city.js b/build/libs/enrollment/city.js
new file mode 100644
index 00000000..9a390c31
--- /dev/null
+++ b/build/libs/enrollment/city.js
@@ -0,0 +1 @@
+"use strict";
\ No newline at end of file
diff --git a/build/libs/enrollment/common.js b/build/libs/enrollment/common.js
new file mode 100644
index 00000000..9c788951
--- /dev/null
+++ b/build/libs/enrollment/common.js
@@ -0,0 +1,15 @@
+'use strict';
+
+var libs = process.cwd() + '/libs';
+
+var log = require(libs + '/log')(module);
+
+var sqlDecorator = require(libs + '/query_decorator');
+
+var yearRange = function yearRange() {
+    var yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)' + 'AS end_year FROM turmas AS t';
+    log.debug('Generated SQL query for enrollments\' year range');
+    return sqlDecorator.execQuery(yearSql, []);
+};
+
+module.exports = yearRange;
\ No newline at end of file
diff --git a/build/libs/enrollment/country.js b/build/libs/enrollment/country.js
new file mode 100644
index 00000000..9a390c31
--- /dev/null
+++ b/build/libs/enrollment/country.js
@@ -0,0 +1 @@
+"use strict";
\ No newline at end of file
diff --git a/build/libs/enrollment/region.js b/build/libs/enrollment/region.js
new file mode 100644
index 00000000..9a390c31
--- /dev/null
+++ b/build/libs/enrollment/region.js
@@ -0,0 +1 @@
+"use strict";
\ No newline at end of file
diff --git a/build/libs/enrollment/state.js b/build/libs/enrollment/state.js
new file mode 100644
index 00000000..9a390c31
--- /dev/null
+++ b/build/libs/enrollment/state.js
@@ -0,0 +1 @@
+"use strict";
\ No newline at end of file
diff --git a/build/libs/log.js b/build/libs/log.js
new file mode 100644
index 00000000..e93d2ff2
--- /dev/null
+++ b/build/libs/log.js
@@ -0,0 +1,33 @@
+'use strict';
+
+var winston = require('winston');
+
+winston.emitErrs = true;
+
+function getFilePath(module) {
+    // using filename in log statements
+    return module.filename.split('/').slice(-2).join('/');
+}
+
+function logger(module) {
+    return new winston.Logger({
+        transports: [new winston.transports.File({
+            level: 'info',
+            filename: process.cwd() + '/logs/all.log',
+            handleException: true,
+            json: false,
+            maxSize: 5242880, // 5MB
+            maxFiles: 2,
+            colorize: false
+        }), new winston.transports.Console({
+            level: 'debug',
+            label: getFilePath(module),
+            handleException: true,
+            json: true,
+            colorize: true
+        })],
+        exitOnError: false
+    });
+}
+
+module.exports = logger;
\ No newline at end of file
diff --git a/build/libs/query_decorator.js b/build/libs/query_decorator.js
new file mode 100644
index 00000000..ce0697d7
--- /dev/null
+++ b/build/libs/query_decorator.js
@@ -0,0 +1,22 @@
+"use strict";
+
+var libs = process.cwd() + "/libs";
+var log = require(libs + "/log")(module);
+var conn = require(libs + "/db/monet");
+
+/**
+ * Basic decorator to wrap SQL query strings
+ */
+exports.execQuery = function (sqlQuery, sqlQueryParams) {
+    log.debug("Executing SQL query '" + sqlQuery + "' with params '" + sqlQueryParams + "'");
+    conn.prepare(sqlQuery, true).then(function (dbQuery) {
+        dbQuery.exec(sqlQueryParams).then(function (dbResult) {
+            log.debug(dbResult.data);
+            log.debug("Query result: " + dbResult.data);
+            return dbResult;
+        }, function (error) {
+            log.error("SQL query execution error: " + error.message);
+            return error;
+        });
+    });
+};
\ No newline at end of file
diff --git a/build/libs/routes/api.js b/build/libs/routes/api.js
new file mode 100644
index 00000000..812eae43
--- /dev/null
+++ b/build/libs/routes/api.js
@@ -0,0 +1,308 @@
+'use strict';
+
+var express = require('express');
+
+var xml = require('js2xmlparser');
+
+var enrollmentApp = express();
+
+var libs = process.cwd() + '/libs';
+
+var log = require(libs + '/log')(module);
+
+var conn = require(libs + '/db/monet');
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ state: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
+
+enrollmentApp.get('/', function (req, res) {
+    res.json({ msg: 'SimCAQ API is running' });
+});
+
+/**
+ * Complete range of the enrollments dataset
+ *
+ * Returns a tuple of start and ending years of the complete enrollments dataset.
+ */
+enrollmentApp.get('/year_range', function (req, res) {
+    var yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)' + 'AS end_year FROM turmas AS t';
+
+    conn.query(yearSql, true).then(function (dbResult) {
+        log.debug(dbResult);
+        req.result = dbResult;
+        response(req, res);
+    }, function (dbError) {
+        log.error('SQL query execution error: ' + dbError.message);
+        // FIXME: change response to HTTP 501 status
+        res.json({ error: 'An internal error has occurred' }).end();
+    });
+});
+
+enrollmentApp.get('/education_level', function (req, res) {
+    var edLevelSql = 'SELECT pk_etapa_ensino_id AS id, desc_etapa AS ' + 'education_level FROM etapa_ensino';
+    conn.query(edLevelSql, true).then(function (dbResult) {
+        log.debug(dbResult);
+        req.result = dbResult;
+        response(req, res);
+    }, function (dbError) {
+        log.error('SQL query error: ' + dbError.message);
+        // FIXME: change response to HTTP 501 status
+        res.json({ error: 'An internal error has occurred' }).end();
+    });
+});
+
+enrollmentApp.get('/data', function (req, res) {
+    log.debug(req.query);
+    log.debug(req.query.met);
+    log.debug(req.query.dim);
+    var schoolClassSql = 'SELECT * FROM turmas';
+    conn.query(schoolClassSql, true).then(function (dbResult) {
+        log.debug(dbResult);
+        req.result = dbResult;
+        response(req, res);
+    }, function (dbError) {
+        log.error('SQL query error: ' + dbError.message);
+        // FIXME: change response to HTTP 501 status
+        res.json({ error: 'An internal error has occurred' }).end();
+    });
+});
+
+enrollmentApp.use('/enrollments', function (req, res, next) {
+    var params = req.query;
+    req.paramCnt = 0;
+
+    if (typeof params.id !== 'undefined') {
+        req.id = parseInt(params.id, 10);
+        req.paramCnt += 1;
+    }
+
+    if (typeof params.location_id !== 'undefined') {
+        req.location_id = parseInt(params.location_id, 10);
+        req.paramCnt += 1;
+    }
+
+    if (typeof params.adm_dependency_id !== 'undefined') {
+        req.adm_dependency_id = parseInt(params.adm_dependency_id, 10);
+        req.paramCnt += 1;
+    }
+
+    if (typeof params.census_year !== 'undefined') {
+        req.census_year = parseInt(params.census_year, 10);
+        req.paramCnt += 1;
+    }
+
+    if (typeof params.education_level_id !== 'undefined') {
+        req.education_level_id = parseInt(params.education_level_id, 10);
+        req.paramCnt += 1;
+    }
+
+    next();
+});
+
+enrollmentApp.use('/enrollments', function (req, res, next) {
+    var params = req.query;
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
+        log.debug('Using enrollments query for regions');
+        req.sqlQuery = 'SELECT r.nome AS name, COALESCE(SUM(t.num_matriculas), 0) AS total ' + 'FROM regioes AS r ' + 'INNER JOIN estados AS e ON r.pk_regiao_id = e.fk_regiao_id ' + 'INNER JOIN municipios AS m ON e.pk_estado_id = m.fk_estado_id ' + 'LEFT OUTER JOIN turmas AS t ON ( ' + 'm.pk_municipio_id = t.fk_municipio_id ';
+        req.sqlQueryParams = [];
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
+        req.sqlQuery += ')';
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'r.pk_regiao_id = ?';
+            req.sqlQueryParams.push(req.id);
+        }
+        req.sqlQuery += ' GROUP BY r.nome';
+    }
+    next();
+});
+
+enrollmentApp.use('/enrollments', function (req, res, next) {
+    var params = req.query;
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
+        log.debug('Using enrollments query for states');
+        req.sqlQuery = 'SELECT e.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total ' + 'FROM estados AS e ' + 'INNER JOIN municipios AS m ON m.fk_estado_id = e.pk_estado_id ' + 'LEFT OUTER JOIN turmas AS t ON (' + 'm.pk_municipio_id = t.fk_municipio_id ';
+        req.sqlQueryParams = [];
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
+        req.sqlQuery += ')';
+
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'e.pk_estado_id = ?';
+            req.sqlQueryParams.push(req.id);
+        }
+
+        req.sqlQuery += ' GROUP BY e.nome';
+    }
+    next();
+});
+
+enrollmentApp.use('/enrollments', function (req, res, next) {
+    var params = req.query;
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
+        log.debug('Using enrollments query for cities');
+        req.sqlQuery = 'SELECT m.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total ' + 'FROM municipios AS m ' + 'LEFT OUTER JOIN turmas AS t ON ( ' + 'm.pk_municipio_id = t.fk_municipio_id';
+        req.sqlQueryParams = [];
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
+        req.sqlQuery += ')';
+
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'm.pk_municipio_id = ?';
+            req.sqlQueryParams.push(req.id);
+        }
+
+        req.sqlQuery += 'GROUP BY m.nome';
+    }
+    next();
+});
+
+enrollmentApp.use('/enrollments', function (req, res, next) {
+    var params = req.query;
+    if (typeof params.aggregate === 'undefined') {
+        log.debug('Using enrollments query for the whole country');
+        req.sqlQuery = 'SELECT \'Brasil\' AS name, COALESCE(SUM(t.num_matriculas),0) AS total ' + 'FROM turmas AS t';
+        req.sqlQueryParams = [];
+
+        if (req.paramCnt > 0) {
+            req.sqlQuery += ' WHERE ';
+        }
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+    }
+    next();
+});
+
+enrollmentApp.get('/enrollments', function (req, res, next) {
+    log.debug('Request parameters: ' + req);
+    if (typeof req.sqlQuery === 'undefined') {
+        // Should only happen if there is a bug in the chaining of the
+        // '/enrollments' route, since when no +aggregate+ parameter is given,
+        // it defaults to use the query for the whole country.
+        log.error('BUG -- No SQL query was found to be executed!');
+        res.send('Request could not be satisfied due to an internal error');
+    } else {
+        log.debug('SQL query: ${ req.sqlQuery }?');
+        log.debug(req.sqlQuery);
+
+        conn.prepare(req.sqlQuery, true).then(function (dbQuery) {
+            dbQuery.exec(req.sqlQueryParams).then(function (dbResult) {
+                log.debug(dbResult);
+                req.result = dbResult;
+                response(req, res);
+            }, function (dbError) {
+                log.error('SQL query execution error: ' + dbError.message);
+                // FIXME: change response to HTTP 501 status
+                res.json({ error: 'An internal error has occurred' }).end();
+            });
+        });
+    }
+});
+
+module.exports = enrollmentApp;
\ No newline at end of file
diff --git a/build/libs/routes/cities.js b/build/libs/routes/cities.js
new file mode 100644
index 00000000..f549624e
--- /dev/null
+++ b/build/libs/routes/cities.js
@@ -0,0 +1,85 @@
+'use strict';
+
+var express = require('express');
+
+var xml = require('js2xmlparser');
+
+var cityApp = express();
+
+var libs = process.cwd() + '/libs';
+
+var log = require(libs + '/log')(module);
+
+var conn = require(libs + '/db/monet');
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ city: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
+
+cityApp.get('/', function (req, res) {
+    conn.query('SELECT * FROM municipios', true).then(function (dbResult) {
+        log.debug(dbResult);
+        req.result = dbResult;
+        response(req, res);
+    }, function (dbError) {
+        log.error('SQL query execution error: ' + dbError.message);
+        // FIXME: change response to HTTP 501 status
+        res.json({ error: 'An internal error has occurred' }).end();
+    });
+});
+
+cityApp.get('/:id', function (req, res) {
+    var citySql = 'SELECT * FROM municipios WHERE pk_municipio_id = ?';
+    var cityId = parseInt(req.params.id, 10);
+    conn.prepare(citySql, true).then(function (dbQuery) {
+        dbQuery.exec([cityId]).then(function (dbResult) {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        }, function (dbError) {
+            log.error('SQL query execution error: ' + dbError.message);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        });
+    });
+});
+
+cityApp.get('/ibge/:id', function (req, res) {
+    var citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
+    var cityId = parseInt(req.params.id, 10);
+    conn.prepare(citySql, true).then(function (dbQuery) {
+        dbQuery.exec([cityId]).then(function (dbResult) {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        }, function (dbError) {
+            log.error('SQL query execution error: ' + dbError.message);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        });
+    });
+});
+
+cityApp.get('/state/:id', function (req, res) {
+    var citySql = 'SELECT * FROM municipios WHERE fk_estado_id = ?';
+    var stateId = parseInt(req.params.id, 10);
+    conn.prepare(citySql, true).then(function (dbQuery) {
+        dbQuery.exec([stateId]).then(function (dbResult) {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        }, function (dbError) {
+            log.error('SQL query execution error: ' + dbError.message);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        });
+    });
+});
+
+module.exports = cityApp;
\ No newline at end of file
diff --git a/build/libs/routes/regions.js b/build/libs/routes/regions.js
new file mode 100644
index 00000000..75cf2bee
--- /dev/null
+++ b/build/libs/routes/regions.js
@@ -0,0 +1,54 @@
+'use strict';
+
+var express = require('express');
+
+var xml = require('js2xmlparser');
+
+var regionApp = express();
+
+var libs = process.cwd() + '/libs';
+
+var log = require(libs + '/log')(module);
+
+var conn = require(libs + '/db/monet');
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ state: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
+
+regionApp.get('/', function (req, res) {
+    var regionSql = 'SELECT * FROM regioes';
+    conn.query(regionSql, true).then(function (dbResult) {
+        log.debug(dbResult);
+        req.result = dbResult;
+        response(req, res);
+    }, function (dbError) {
+        log.error('SQL query execution error: ' + dbError.message);
+        // FIXME: change response to HTTP 501 status
+        res.json({ error: 'An internal error has occurred' }).end();
+    });
+});
+
+regionApp.get('/:id', function (req, res) {
+    var regionSql = 'SELECT * FROM regioes WHERE pk_regiao_id = ?';
+    var regionId = parseInt(req.params.id, 10);
+    conn.prepare(regionSql, true).then(function (dbQuery) {
+        dbQuery.exec([regionId]).then(function (dbResult) {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        }, function (dbError) {
+            log.error('SQL query execution error: ' + dbError.message);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        });
+    });
+});
+
+module.exports = regionApp;
\ No newline at end of file
diff --git a/build/libs/routes/states.js b/build/libs/routes/states.js
new file mode 100644
index 00000000..8579f392
--- /dev/null
+++ b/build/libs/routes/states.js
@@ -0,0 +1,70 @@
+'use strict';
+
+var express = require('express');
+
+var xml = require('js2xmlparser');
+
+var stateApp = express();
+
+var libs = process.cwd() + '/libs';
+
+var log = require(libs + '/log')(module);
+
+var conn = require(libs + '/db/monet');
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ state: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
+
+stateApp.get('/', function (req, res, next) {
+    var stateSql = 'SELECT * FROM estados';
+    conn.query(stateSql, true).then(function (dbResult) {
+        log.debug(dbResult);
+        req.result = dbResult;
+        response(req, res);
+    }, function (dbError) {
+        log.error('SQL query execution error: ' + dbError.message);
+        // FIXME: change response to HTTP 501 status
+        res.json({ error: 'An internal error has occurred' }).end();
+    });
+});
+
+stateApp.get('/:id', function (req, res, next) {
+    var stateSql = 'SELECT * FROM estados WHERE pk_estado_id = ?';
+    var stateId = parseInt(req.params.id, 10);
+    conn.prepare(stateSql, true).then(function (dbQuery) {
+        dbQuery.exec([stateId]).then(function (dbResult) {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        }, function (dbError) {
+            log.error('SQL query execution error: ' + dbError.message);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        });
+    });
+});
+
+stateApp.get('/region/:id', function (req, res, next) {
+    var stateSql = 'SELECT * FROM estados WHERE fk_regiao_id = ?';
+    var regionId = parseInt(req.params.id, 10);
+    conn.prepare(stateSql, true).then(function (dbQuery) {
+        dbQuery.exec([regionId]).then(function (dbResult) {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        }, function (dbError) {
+            log.error('SQL query execution error: ' + dbError.message);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        });
+    });
+});
+
+module.exports = stateApp;
\ No newline at end of file
diff --git a/build/logs/.gitignore b/build/logs/.gitignore
new file mode 100644
index 00000000..397b4a76
--- /dev/null
+++ b/build/logs/.gitignore
@@ -0,0 +1 @@
+*.log
diff --git a/build/server.js b/build/server.js
new file mode 100644
index 00000000..5f2171cc
--- /dev/null
+++ b/build/server.js
@@ -0,0 +1,18 @@
+'use strict';
+
+var debug = require('debug')('node-express-base');
+
+var libs = process.cwd() + '/libs';
+
+var config = require(libs + '/config');
+
+var log = require(libs + '/log')(module);
+
+var app = require(libs + '/app');
+
+app.set('port', config.get('port') || 3000);
+
+var server = app.listen(app.get('port'), function () {
+    debug('Express server listening on port ' + server.address().port);
+    log.info('Express server listening on port ' + config.get('port'));
+});
\ No newline at end of file
diff --git a/build/test/test.js b/build/test/test.js
new file mode 100644
index 00000000..55caa55c
--- /dev/null
+++ b/build/test/test.js
@@ -0,0 +1,139 @@
+'use strict';
+
+var chai = require('chai');
+var chaiHttp = require('chai-http');
+var assert = chai.assert;
+var expect = chai.expect;
+var should = chai.should(); // actually call the function
+var server = require('../libs/app');
+
+chai.use(chaiHttp);
+
+describe('request enrollments', function () {
+    it('should list enrollments', function (done) {
+        chai.request(server).get('/v1/enrollments').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result[0].should.have.property('name');
+            res.body.result[0].should.have.property('total');
+            done();
+        });
+    });
+});
+
+describe('request regions', function () {
+    it('should list all regions', function (done) {
+        chai.request(server).get('/v1/regions').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result[0].should.have.property('pk_regiao_id');
+            res.body.result[0].should.have.property('nome');
+            done();
+        });
+    });
+
+    it('should list region by id', function (done) {
+        chai.request(server).get('/v1/regions/1').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result.should.have.length(1);
+            res.body.result[0].should.have.property('pk_regiao_id');
+            res.body.result[0].should.have.property('nome');
+            done();
+        });
+    });
+});
+
+describe('request states', function () {
+
+    it('should list all states', function (done) {
+        chai.request(server).get('/v1/states').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result[0].should.have.property('pk_estado_id');
+            res.body.result[0].should.have.property('fk_regiao_id');
+            res.body.result[0].should.have.property('nome');
+            done();
+        });
+    });
+
+    it('should list a state by id', function (done) {
+        chai.request(server).get('/v1/states/11').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result.should.have.length(1);
+            res.body.result[0].should.have.property('pk_estado_id');
+            res.body.result[0].should.have.property('fk_regiao_id');
+            res.body.result[0].should.have.property('nome');
+            done();
+        });
+    });
+
+    it('should list states by region id', function (done) {
+        chai.request(server).get('/v1/states/region/1').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result[0].should.have.property('pk_estado_id');
+            res.body.result[0].should.have.property('fk_regiao_id');
+            res.body.result[0].should.have.property('nome');
+            done();
+        });
+    });
+});
+
+describe('request cities', function () {
+
+    it('should list all cities', function (done) {
+        chai.request(server).get('/v1/cities').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result[0].should.have.property('pk_municipio_id');
+            res.body.result[0].should.have.property('fk_estado_id');
+            res.body.result[0].should.have.property('nome');
+            res.body.result[0].should.have.property('codigo_ibge');
+            done();
+        });
+    });
+
+    it('should list a city by id', function (done) {
+        chai.request(server).get('/v1/cities/1').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result[0].should.have.property('pk_municipio_id');
+            res.body.result[0].should.have.property('fk_estado_id');
+            res.body.result[0].should.have.property('nome');
+            res.body.result[0].should.have.property('codigo_ibge');
+            done();
+        });
+    });
+
+    it('should list a city by codigo_ibge', function (done) {
+        chai.request(server).get('/v1/cities/ibge/1200013').end(function (err, res) {
+            res.should.have.status(200);
+            res.should.be.json;
+            res.body.should.have.property('result');
+            res.body.result.should.be.a('array');
+            res.body.result[0].should.have.property('pk_municipio_id');
+            res.body.result[0].should.have.property('fk_estado_id');
+            res.body.result[0].should.have.property('nome');
+            res.body.result[0].should.have.property('codigo_ibge');
+            done();
+        });
+    });
+});
\ No newline at end of file
diff --git a/config.json b/config.json
index 8f60036a..1da8951e 100644
--- a/config.json
+++ b/config.json
@@ -1,7 +1,7 @@
 {
     "port": 3000,
     "monetdb": {
-        "host": "simcaqdb1",
+        "host": "localhost",
         "port": 50000,
         "dbname": "simcaq_dev",
         "user": "monetdb",
diff --git a/gulpfile.babel.js b/gulpfile.babel.js
new file mode 100644
index 00000000..e07481f9
--- /dev/null
+++ b/gulpfile.babel.js
@@ -0,0 +1,15 @@
+const gulp = require('gulp');
+const babel = require('gulp-babel');
+const eslint = require('gulp-eslint');
+
+gulp.task('default', () => {
+    // run ESLint
+    gulp.src('src/**/*.js')
+        .pipe(eslint())
+        .pipe(eslint.format());
+
+    // compile source to ES5
+    gulp.src('src/**/*.js')
+        .pipe(babel())
+        .pipe(gulp.dest('build'));
+});
diff --git a/libs/app.js b/libs/app.js
deleted file mode 100644
index 544f649b..00000000
--- a/libs/app.js
+++ /dev/null
@@ -1,53 +0,0 @@
-var express = require('express')
-var path = require('path')
-var cookieParser = require('cookie-parser')
-var bodyParser = require('body-parser')
-var csv = require('csv-express')
-var xml = require('js2xmlparser')
-var methodOverride = require('method-override')
-var cors = require('cors')
-
-var libs = process.cwd() + '/libs/'
-
-var config = require('./config')
-var log = require('./log')(module)
-
-var api = require('./routes/api')
-var states = require('./routes/states')
-var regions = require('./routes/regions')
-var cities = require('./routes/cities')
-
-var app = express()
-
-app.use(bodyParser.json())
-app.use(bodyParser.urlencoded({ extended: false }))
-app.use(cookieParser())
-app.use(cors())
-app.use(methodOverride())
-
-app.use('/v1/', api)
-app.use('/v1/states', states)
-app.use('/v1/regions', regions)
-app.use('/v1/cities', cities)
-
-// catch 404 and forward to error handler
-app.use(function(req, res, next){
-    res.status(404)
-    log.debug('%s %d %s', req.method, res.statusCode, req.url)
-    res.json({
-    	error: 'Not found'
-    })
-    return
-})
-
-// error handlers
-app.use(function(err, req, res, next){
-    res.status(err.status || 500)
-    log.error('%s %d %s', req.method, res.statusCode, err.message)
-    res.json({
-    	error: err.message
-    })
-    return
-})
-
-module.exports = app
diff --git a/libs/config.js b/libs/config.js
deleted file mode 100644
index 78f7831a..00000000
--- a/libs/config.js
+++ /dev/null
@@ -1,9 +0,0 @@
-var nconf = require('nconf')
-
-nconf.argv()
-	.env()
-	.file({
-		file: process.cwd() + '/config.json'
-	})
-
-module.exports = nconf
diff --git a/libs/db/monet.js b/libs/db/monet.js
deleted file mode 100644
index 0b554285..00000000
--- a/libs/db/monet.js
+++ /dev/null
@@ -1,19 +0,0 @@
-var mdb = require('monetdb')()
-
-var libs = process.cwd() + '/libs/'
-
-var log = require(libs + 'log')(module)
-var config = require(libs + 'config')
-
-var options = {
-    host: config.get('monetdb:host'),
-    port: config.get('monetdb:port'),
-    dbname: config.get('monetdb:dbname'),
-    user: config.get('monetdb:user'),
-    password: config.get('monetdb:password')
-}
-
-var conn = new mdb(options)
-conn.connect()
-
-module.exports = conn
diff --git a/libs/log.js b/libs/log.js
deleted file mode 100644
index 419b3e4b..00000000
--- a/libs/log.js
+++ /dev/null
@@ -1,35 +0,0 @@
-var winston = require('winston')
-
-winston.emitErrs = true
-
-function logger(module) {
-
-    return new winston.Logger({
-        transports : [
-            new winston.transports.File({
-                level: 'info',
-                filename: process.cwd() + '/logs/all.log',
-                handleException: true,
-                json: false,
-                maxSize: 5242880, //5mb
-                maxFiles: 2,
-                colorize: false
-            }),
-            new winston.transports.Console({
-                level: 'debug',
-                label: getFilePath(module),
-                handleException: true,
-                json: true,
-                colorize: true
-            })
-        ],
-        exitOnError: false
-    })
-}
-
-function getFilePath (module ) {
-    //using filename in log statements
-    return module.filename.split('/').slice(-2).join('/')
-}
-
-module.exports = logger
diff --git a/libs/routes/cities.js b/libs/routes/cities.js
deleted file mode 100644
index 6a222049..00000000
--- a/libs/routes/cities.js
+++ /dev/null
@@ -1,66 +0,0 @@
-var express = require('express')
-var xml = require('js2xmlparser')
-var router = express.Router()
-
-var libs = process.cwd() + '/libs/'
-
-var log = require(libs + 'log')(module)
-var config = require(libs + 'config')
-
-var conn = require(libs + 'db/monet')
-
-function response(req, res) {
-  if (req.query.format === 'csv') {
-    res.csv(req.result.data)
-  } else if (req.query.format === 'xml') {
-    res.send(xml("result", JSON.stringify({city: req.result.data})))
-  }
-  else {
-    res.json({
-        result: req.result.data
-    })
-  }
-}
-
-router.get('/', function(req, res) {
-  conn.query(
-    'SELECT * FROM municipios', true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-
-})
-
-router.get('/:id', function(req, res) {
-  conn.query(
-    'SELECT * FROM municipios WHERE pk_municipio_id='+req.params.id, true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-})
-
-router.get('/ibge/:id', function(req, res) {
-  conn.query(
-    'SELECT * FROM municipios WHERE codigo_ibge='+req.params.id, true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-})
-
-router.get('/state/:id', function(req, res) {
-  conn.query(
-    'SELECT * FROM municipios WHERE fk_estado_id='+req.params.id, true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-})
-
-module.exports = router
diff --git a/libs/routes/regions.js b/libs/routes/regions.js
deleted file mode 100644
index 6c85fccb..00000000
--- a/libs/routes/regions.js
+++ /dev/null
@@ -1,45 +0,0 @@
-var express = require('express')
-var xml = require('js2xmlparser')
-var router = express.Router()
-
-var libs = process.cwd() + '/libs/'
-
-var log = require(libs + 'log')(module)
-var config = require(libs + 'config')
-
-var conn = require(libs + 'db/monet')
-
-function response(req, res) {
-  if (req.query.format === 'csv') {
-    res.csv(req.result.data)
-  } else if (req.query.format === 'xml') {
-    res.send(xml("result", JSON.stringify({state: req.result.data})))
-  }
-  else {
-    res.json({
-        result: req.result.data
-    })
-  }
-}
-
-router.get('/', function(req, res) {
-  conn.query(
-    'SELECT * FROM regioes', true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-})
-
-router.get('/:id', function(req, res) {
-  conn.query(
-    'SELECT * FROM regioes WHERE pk_regiao_id='+req.params.id, true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-})
-
-module.exports = router
diff --git a/libs/routes/states.js b/libs/routes/states.js
deleted file mode 100644
index 2192c537..00000000
--- a/libs/routes/states.js
+++ /dev/null
@@ -1,55 +0,0 @@
-var express = require('express')
-var xml = require('js2xmlparser')
-var router = express.Router()
-
-var libs = process.cwd() + '/libs/'
-
-var log = require(libs + 'log')(module)
-var config = require(libs + 'config')
-
-var conn = require(libs + 'db/monet')
-
-function response(req, res) {
-  if (req.query.format === 'csv') {
-    res.csv(req.result.data)
-  } else if (req.query.format === 'xml') {
-    res.send(xml("result", JSON.stringify({state: req.result.data})))
-  }
-  else {
-    res.json({
-        result: req.result.data
-    })
-  }
-}
-
-router.get('/', function(req, res, next) {
-  conn.query(
-    'SELECT * FROM estados', true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-})
-
-router.get('/:id', function(req, res, next) {
-  conn.query(
-    'SELECT * FROM estados WHERE pk_estado_id='+req.params.id, true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-})
-
-router.get('/region/:id', function(req, res, next) {
-  conn.query(
-    'SELECT * FROM estados WHERE fk_regiao_id='+req.params.id, true
-  ).then(function(result) {
-    log.debug(result)
-    req.result = result
-    response(req, res)
-  })
-})
-
-module.exports = router
diff --git a/package.json b/package.json
index fd0765bb..cee6b8e0 100644
--- a/package.json
+++ b/package.json
@@ -19,14 +19,25 @@
     "forever": "^0.15.2",
     "js2xmlparser": "^1.0.0",
     "method-override": "^2.3.3",
-    "monetdb": "^1.1.2",
+    "monetdb-pool": "0.0.8",
     "nconf": "^0.6.x",
     "winston": "^2.2.0"
   },
   "license": "MIT",
   "devDependencies": {
+    "babel-preset-es2015": "^6.13.2",
+    "babelify": "^7.3.0",
+    "browserify": "^13.1.0",
     "chai": "^3.5.0",
     "chai-http": "^3.0.0",
+    "eslint": "^3.3.1",
+    "eslint-config-airbnb": "^10.0.1",
+    "eslint-plugin-import": "^1.13.0",
+    "eslint-plugin-jsx-a11y": "^2.1.0",
+    "eslint-plugin-react": "^6.1.1",
+    "gulp": "^3.9.1",
+    "gulp-babel": "^6.1.2",
+    "gulp-eslint": "^3.0.1",
     "mocha": "^2.5.3"
   }
 }
diff --git a/server.js b/server.js
deleted file mode 100644
index dc0c1ce3..00000000
--- a/server.js
+++ /dev/null
@@ -1,13 +0,0 @@
-var debug = require('debug')('node-express-base')
-
-var libs = process.cwd() + '/libs/'
-var config = require(libs + 'config')
-var log = require(libs + 'log')(module)
-var app = require(libs + 'app')
-
-app.set('port', config.get('port') || 3000)
-
-var server = app.listen(app.get('port'), function() {
-  debug('Express server listening on port ' + server.address().port)
-  log.info('Express server listening on port ' + config.get('port'))
-})
diff --git a/src/libs/app.js b/src/libs/app.js
new file mode 100644
index 00000000..c2396420
--- /dev/null
+++ b/src/libs/app.js
@@ -0,0 +1,41 @@
+const express = require('express');
+const cookieParser = require('cookie-parser');
+const bodyParser = require('body-parser');
+const methodOverride = require('method-override');
+const cors = require('cors');
+
+const log = require('./log')(module);
+
+const api = require('./routes/api');
+const states = require('./routes/states');
+const regions = require('./routes/regions');
+const cities = require('./routes/cities');
+
+const app = express();
+
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({ extended: false }));
+app.use(cookieParser());
+app.use(cors());
+app.use(methodOverride());
+
+app.use('/v1/', api);
+app.use('/v1/states', states);
+app.use('/v1/regions', regions);
+app.use('/v1/cities', cities);
+
+// catch 404 and forward to error handler
+app.use((req, res, next) => {
+    res.status(404);
+    log.debug('%s %d %s', req.method, res.statusCode, req.url);
+    res.json({ error: 'Not found' }).end();
+});
+
+// error handlers
+app.use((err, req, res, next) => {
+    res.status(err.status || 500);
+    log.error('%s %d %s', req.method, res.statusCode, err.message);
+    res.json({ error: err.message }).end();
+});
+
+module.exports = app;
diff --git a/src/libs/config.js b/src/libs/config.js
new file mode 100644
index 00000000..ef5a26cd
--- /dev/null
+++ b/src/libs/config.js
@@ -0,0 +1,7 @@
+const nconf = require('nconf');
+
+nconf.argv()
+    .env()
+    .file({ file: `${process.cwd()}/config.json` });
+
+module.exports = nconf;
diff --git a/src/libs/db/monet.js b/src/libs/db/monet.js
new file mode 100644
index 00000000..1cf874cc
--- /dev/null
+++ b/src/libs/db/monet.js
@@ -0,0 +1,22 @@
+const MonetDBPool = require('monetdb-pool');
+
+const libs = `${process.cwd()}/libs`;
+
+const config = require(`${libs}/config`);
+
+const poolOptions = {
+    nrConnections: config.get('monetdb:nrConnections'),
+};
+
+const options = {
+    host: config.get('monetdb:host'),
+    port: config.get('monetdb:port'),
+    dbname: config.get('monetdb:dbname'),
+    user: config.get('monetdb:user'),
+    password: config.get('monetdb:password'),
+};
+
+const conn = new MonetDBPool(poolOptions, options);
+conn.connect();
+
+module.exports = conn;
diff --git a/src/libs/db/query_decorator.js b/src/libs/db/query_decorator.js
new file mode 100644
index 00000000..361f3509
--- /dev/null
+++ b/src/libs/db/query_decorator.js
@@ -0,0 +1,25 @@
+const libs = `${process.cwd()}/libs`;
+const log = require(`${libs}/log`)(module);
+const conn = require(`${libs}/db/monet`);
+
+/**
+ * Basic decorator to wrap SQL query strings
+ */
+exports.execQuery = (sqlQuery, sqlQueryParams) => {
+    log.debug(`Executing SQL query '${sqlQuery}' with params '${sqlQueryParams}'`);
+    conn.prepare(sqlQuery, true).then(
+        (dbQuery) => {
+            dbQuery.exec(sqlQueryParams).then(
+                (dbResult) => {
+                    log.debug(dbResult.data);
+                    log.debug(`Query result: ${dbResult.data}`);
+                    return dbResult;
+                },
+                (error) => {
+                    log.error(`SQL query execution error: ${error.message}`);
+                    return error;
+                }
+            );
+        }
+    );
+};
diff --git a/src/libs/enrollment/all.js b/src/libs/enrollment/all.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/libs/enrollment/city.js b/src/libs/enrollment/city.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/libs/enrollment/common.js b/src/libs/enrollment/common.js
new file mode 100644
index 00000000..207f44c3
--- /dev/null
+++ b/src/libs/enrollment/common.js
@@ -0,0 +1,14 @@
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const sqlDecorator = require(`${libs}/query_decorator`);
+
+const yearRange = () => {
+    const yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)'
+        + 'AS end_year FROM turmas AS t';
+    log.debug('Generated SQL query for enrollments\' year range');
+    return sqlDecorator.execQuery(yearSql, []);
+};
+
+module.exports = yearRange;
diff --git a/src/libs/enrollment/country.js b/src/libs/enrollment/country.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/libs/enrollment/region.js b/src/libs/enrollment/region.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/libs/enrollment/state.js b/src/libs/enrollment/state.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/libs/log.js b/src/libs/log.js
new file mode 100644
index 00000000..0eb64119
--- /dev/null
+++ b/src/libs/log.js
@@ -0,0 +1,34 @@
+const winston = require('winston');
+
+winston.emitErrs = true;
+
+function getFilePath(module) {
+    // using filename in log statements
+    return module.filename.split('/').slice(-2).join('/');
+}
+
+function logger(module) {
+    return new winston.Logger({
+        transports: [
+            new winston.transports.File({
+                level: 'info',
+                filename: `${process.cwd()}/logs/all.log`,
+                handleException: true,
+                json: false,
+                maxSize: 5242880, // 5MB
+                maxFiles: 2,
+                colorize: false,
+            }),
+            new winston.transports.Console({
+                level: 'debug',
+                label: getFilePath(module),
+                handleException: true,
+                json: true,
+                colorize: true,
+            }),
+        ],
+        exitOnError: false,
+    });
+}
+
+module.exports = logger;
diff --git a/libs/routes/api.js b/src/libs/routes/api.js
similarity index 65%
rename from libs/routes/api.js
rename to src/libs/routes/api.js
index 9911bf10..d5c46ef6 100644
--- a/libs/routes/api.js
+++ b/src/libs/routes/api.js
@@ -1,20 +1,27 @@
-'use strict';
-
 const express = require('express');
+
 const xml = require('js2xmlparser');
-const enrollmentsApp = express();
 
-const libs = process.cwd() + '/libs/';
+const enrollmentApp = express();
+
+const libs = `${process.cwd()}/libs`;
 
-const log = require(libs + 'log')(module);
-const config = require(libs + 'config');
+const log = require(`${libs}/log`)(module);
 
-const conn = require(libs + 'db/monet');
+const conn = require(`${libs}/db/monet`);
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ state: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
 
-enrollmentsApp.get('/', function (req, res) {
-    res.json({
-        msg: 'SimCAQ API is running'
-    });
+enrollmentApp.get('/', (req, res) => {
+    res.json({ msg: 'SimCAQ API is running' });
 });
 
 /**
@@ -22,69 +29,61 @@ enrollmentsApp.get('/', function (req, res) {
  *
  * Returns a tuple of start and ending years of the complete enrollments dataset.
  */
-enrollmentsApp.get('/year_range', function(req, res) {
-    var yearSql = "SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo) AS end_year FROM turmas AS t";
-    conn.query(yearSql, true).then(function(result) {
-      if (req.query.format === 'csv') {
-        res.csv(result.data);
-      } else if (req.query.format === 'xml') {
-        res.send(xml("result", JSON.stringify({year_range: result.data})))
-      }
-      else {
-        res.json({
-            result: result.data
-        });
-      }
-    }, function(error) {
-        log.error('SQL query error: ${ error.message }?');
-        log.debug(error);
-        res.send('Request could not be satisfied due to an internal error');
-    });
+enrollmentApp.get('/year_range', (req, res) => {
+    const yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)'
+        + 'AS end_year FROM turmas AS t';
+
+    conn.query(yearSql, true).then(
+        (dbResult) => {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        },
+        (dbError) => {
+            log.error(`SQL query execution error: ${dbError.message}`);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        }
+    );
 });
 
-enrollmentsApp.get('/education_level', function(req, res) {
-    var edLevelId = "SELECT pk_etapa_ensino_id AS id, desc_etapa AS education_level FROM etapa_ensino";
-    conn.query(edLevelId, true).then(function(result) {
-      if (req.query.format === 'csv') {
-        res.csv(result.data);
-      } else if (req.query.format === 'xml') {
-        res.send(xml("result", JSON.stringify({year_range: result.data})))
-      }
-      else {
-        res.json({
-            result: result.data
-        });
-      }
-    }, function(error) {
-        log.error('SQL query error: ${ error.message }?');
-        log.debug(error);
-        res.send('Request could not be satisfied due to an internal error');
-    });
+enrollmentApp.get('/education_level', (req, res) => {
+    const edLevelSql = 'SELECT pk_etapa_ensino_id AS id, desc_etapa AS '
+        + 'education_level FROM etapa_ensino';
+    conn.query(edLevelSql, true).then(
+        (dbResult) => {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        },
+        (dbError) => {
+            log.error(`SQL query error: ${dbError.message}`);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        }
+    );
 });
 
-enrollmentsApp.get('/data', function(req, res) {
-    log.debug(req.query)
-    log.debug(req.query.met)
-    log.debug(req.query.dim)
-    conn.query('SELECT * FROM turmas LIMIT 5', true).then(function(result) {
-      if (req.query.format === 'csv') {
-        res.csv(result.data);
-      } else if (req.query.format === 'xml') {
-        res.send(xml("result", JSON.stringify({data: result.data})))
-      }
-      else {
-        res.json({
-            result: result.data
-        });
-      }
-    }, function(error) {
-        log.error('SQL query error: ${ error.message }?');
-        log.debug(error);
-        res.send('Request could not be satisfied due to an internal error');
-    });
-})
+enrollmentApp.get('/data', (req, res) => {
+    log.debug(req.query);
+    log.debug(req.query.met);
+    log.debug(req.query.dim);
+    const schoolClassSql = 'SELECT * FROM turmas';
+    conn.query(schoolClassSql, true).then(
+        (dbResult) => {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        },
+        (dbError) => {
+            log.error(`SQL query error: ${dbError.message}`);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        }
+    );
+});
 
-enrollmentsApp.use('/enrollments', function(req, res, next) {
+enrollmentApp.use('/enrollments', (req, res, next) => {
     const params = req.query;
     req.paramCnt = 0;
 
@@ -116,7 +115,7 @@ enrollmentsApp.use('/enrollments', function(req, res, next) {
     next();
 });
 
-enrollmentsApp.use('/enrollments', function(req, res, next) {
+enrollmentApp.use('/enrollments', (req, res, next) => {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
         log.debug('Using enrollments query for regions');
@@ -163,7 +162,7 @@ enrollmentsApp.use('/enrollments', function(req, res, next) {
     next();
 });
 
-enrollmentsApp.use('/enrollments', function(req, res, next) {
+enrollmentApp.use('/enrollments', (req, res, next) => {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
         log.debug('Using enrollments query for states');
@@ -201,8 +200,8 @@ enrollmentsApp.use('/enrollments', function(req, res, next) {
         req.sqlQuery += ')';
 
         if (typeof req.id !== 'undefined') {
-            req.sqlQuery += " WHERE ";
-            req.sqlQuery += "e.pk_estado_id = ?";
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'e.pk_estado_id = ?';
             req.sqlQueryParams.push(req.id);
         }
 
@@ -211,7 +210,7 @@ enrollmentsApp.use('/enrollments', function(req, res, next) {
     next();
 });
 
-enrollmentsApp.use('/enrollments', function(req, res, next) {
+enrollmentApp.use('/enrollments', (req, res, next) => {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
         log.debug('Using enrollments query for cities');
@@ -248,8 +247,8 @@ enrollmentsApp.use('/enrollments', function(req, res, next) {
         req.sqlQuery += ')';
 
         if (typeof req.id !== 'undefined') {
-            req.sqlQuery += " WHERE ";
-            req.sqlQuery += "m.pk_municipio_id = ?";
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'm.pk_municipio_id = ?';
             req.sqlQueryParams.push(req.id);
         }
 
@@ -258,7 +257,7 @@ enrollmentsApp.use('/enrollments', function(req, res, next) {
     next();
 });
 
-enrollmentsApp.use('/enrollments', function(req, res, next) {
+enrollmentApp.use('/enrollments', (req, res, next) => {
     const params = req.query;
     if (typeof params.aggregate === 'undefined') {
         log.debug('Using enrollments query for the whole country');
@@ -302,35 +301,33 @@ enrollmentsApp.use('/enrollments', function(req, res, next) {
     next();
 });
 
-enrollmentsApp.get('/enrollments', function(req, res, next) {
-    log.debug('Request parameters: ${ req }?');
+enrollmentApp.get('/enrollments', (req, res, next) => {
+    log.debug(`Request parameters: ${req}`);
     if (typeof req.sqlQuery === 'undefined') {
-        /* Should only happen if there is a bug in the chaining of the
-         * '/enrollments' route, since when no +aggregate+ parameter is given,
-         * it defaults to use the query for the whole country.
-         */
+        // Should only happen if there is a bug in the chaining of the
+        // '/enrollments' route, since when no +aggregate+ parameter is given,
+        // it defaults to use the query for the whole country.
         log.error('BUG -- No SQL query was found to be executed!');
         res.send('Request could not be satisfied due to an internal error');
     } else {
         log.debug('SQL query: ${ req.sqlQuery }?');
         log.debug(req.sqlQuery);
-        conn.prepare(req.sqlQuery, true).then(function(dbQuery) {
-            dbQuery.exec(req.sqlQueryParams).then(function(dbResult) {
-                log.debug(dbResult);
-                if (req.query.format === 'csv') {
-                    res.csv(dbResult.data);
-                } else if (req.query.format === 'xml') {
-                    res.send(xml('result', JSON.stringify({enrollments: dbResult.data})));
-                } else {
-                    res.json({ result: dbResult.data });
+
+        conn.prepare(req.sqlQuery, true).then((dbQuery) => {
+            dbQuery.exec(req.sqlQueryParams).then(
+                (dbResult) => {
+                    log.debug(dbResult);
+                    req.result = dbResult;
+                    response(req, res);
+                },
+                (dbError) => {
+                    log.error(`SQL query execution error: ${dbError.message}`);
+                    // FIXME: change response to HTTP 501 status
+                    res.json({ error: 'An internal error has occurred' }).end();
                 }
-            });
-        }, function(error) {
-                log.error('SQL query error: ${ error.message }?');
-                log.debug(error);
-                res.send('Request could not be satisfied due to an internal error');
+            );
         });
     }
 });
 
-module.exports = enrollmentsApp
+module.exports = enrollmentApp;
diff --git a/src/libs/routes/cities.js b/src/libs/routes/cities.js
new file mode 100644
index 00000000..1ae6c48d
--- /dev/null
+++ b/src/libs/routes/cities.js
@@ -0,0 +1,95 @@
+const express = require('express');
+
+const xml = require('js2xmlparser');
+
+const cityApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const conn = require(`${libs}/db/monet`);
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ city: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
+
+cityApp.get('/', (req, res) => {
+    conn.query('SELECT * FROM municipios', true).then(
+        (dbResult) => {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        },
+        (dbError) => {
+            log.error(`SQL query execution error: ${dbError.message}`);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        }
+    );
+});
+
+cityApp.get('/:id', (req, res) => {
+    const citySql = 'SELECT * FROM municipios WHERE pk_municipio_id = ?';
+    const cityId = parseInt(req.params.id, 10);
+    conn.prepare(citySql, true).then((dbQuery) => {
+        dbQuery.exec([cityId]).then(
+            (dbResult) => {
+                log.debug(dbResult);
+                req.result = dbResult;
+                response(req, res);
+            },
+            (dbError) => {
+                log.error(`SQL query execution error: ${dbError.message}`);
+                // FIXME: change response to HTTP 501 status
+                res.json({ error: 'An internal error has occurred' }).end();
+            }
+        );
+    });
+});
+
+cityApp.get('/ibge/:id', (req, res) => {
+    const citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
+    const cityId = parseInt(req.params.id, 10);
+    conn.prepare(citySql, true).then((dbQuery) => {
+        dbQuery.exec([cityId]).then(
+            (dbResult) => {
+                log.debug(dbResult);
+                req.result = dbResult;
+                response(req, res);
+            },
+            (dbError) => {
+                log.error(`SQL query execution error: ${dbError.message}`);
+                // FIXME: change response to HTTP 501 status
+                res.json({ error: 'An internal error has occurred' }).end();
+            }
+        );
+    });
+});
+
+cityApp.get('/state/:id', (req, res) => {
+    const citySql = 'SELECT * FROM municipios WHERE fk_estado_id = ?';
+    const stateId = parseInt(req.params.id, 10);
+    conn.prepare(citySql, true).then((dbQuery) => {
+        dbQuery.exec([stateId]).then(
+            (dbResult) => {
+                log.debug(dbResult);
+                req.result = dbResult;
+                response(req, res);
+            },
+            (dbError) => {
+                log.error(`SQL query execution error: ${dbError.message}`);
+                // FIXME: change response to HTTP 501 status
+                res.json({ error: 'An internal error has occurred' }).end();
+            }
+        );
+    });
+});
+
+module.exports = cityApp;
diff --git a/src/libs/routes/regions.js b/src/libs/routes/regions.js
new file mode 100644
index 00000000..d76037c0
--- /dev/null
+++ b/src/libs/routes/regions.js
@@ -0,0 +1,58 @@
+const express = require('express');
+
+const xml = require('js2xmlparser');
+
+const regionApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const conn = require(`${libs}/db/monet`);
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ state: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
+
+regionApp.get('/', (req, res) => {
+    const regionSql = 'SELECT * FROM regioes';
+    conn.query(regionSql, true).then(
+        (dbResult) => {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        },
+        (dbError) => {
+            log.error(`SQL query execution error: ${dbError.message}`);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        }
+    );
+});
+
+regionApp.get('/:id', (req, res) => {
+    const regionSql = 'SELECT * FROM regioes WHERE pk_regiao_id = ?';
+    const regionId = parseInt(req.params.id, 10);
+    conn.prepare(regionSql, true).then((dbQuery) => {
+        dbQuery.exec([regionId]).then(
+            (dbResult) => {
+                log.debug(dbResult);
+                req.result = dbResult;
+                response(req, res);
+            },
+            (dbError) => {
+                log.error(`SQL query execution error: ${dbError.message}`);
+                // FIXME: change response to HTTP 501 status
+                res.json({ error: 'An internal error has occurred' }).end();
+            }
+        );
+    });
+});
+
+module.exports = regionApp;
diff --git a/src/libs/routes/states.js b/src/libs/routes/states.js
new file mode 100644
index 00000000..23cc7a3d
--- /dev/null
+++ b/src/libs/routes/states.js
@@ -0,0 +1,77 @@
+const express = require('express');
+
+const xml = require('js2xmlparser');
+
+const stateApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const conn = require(`${libs}/db/monet`);
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ state: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
+
+stateApp.get('/', (req, res, next) => {
+    const stateSql = 'SELECT * FROM estados';
+    conn.query(stateSql, true).then(
+        (dbResult) => {
+            log.debug(dbResult);
+            req.result = dbResult;
+            response(req, res);
+        },
+        (dbError) => {
+            log.error(`SQL query execution error: ${dbError.message}`);
+            // FIXME: change response to HTTP 501 status
+            res.json({ error: 'An internal error has occurred' }).end();
+        }
+    );
+});
+
+stateApp.get('/:id', (req, res, next) => {
+    const stateSql = 'SELECT * FROM estados WHERE pk_estado_id = ?';
+    const stateId = parseInt(req.params.id, 10);
+    conn.prepare(stateSql, true).then((dbQuery) => {
+        dbQuery.exec([stateId]).then(
+            (dbResult) => {
+                log.debug(dbResult);
+                req.result = dbResult;
+                response(req, res);
+            },
+            (dbError) => {
+                log.error(`SQL query execution error: ${dbError.message}`);
+                // FIXME: change response to HTTP 501 status
+                res.json({ error: 'An internal error has occurred' }).end();
+            }
+        );
+    });
+});
+
+stateApp.get('/region/:id', (req, res, next) => {
+    const stateSql = 'SELECT * FROM estados WHERE fk_regiao_id = ?';
+    const regionId = parseInt(req.params.id, 10);
+    conn.prepare(stateSql, true).then((dbQuery) => {
+        dbQuery.exec([regionId]).then(
+            (dbResult) => {
+                log.debug(dbResult);
+                req.result = dbResult;
+                response(req, res);
+            },
+            (dbError) => {
+                log.error(`SQL query execution error: ${dbError.message}`);
+                // FIXME: change response to HTTP 501 status
+                res.json({ error: 'An internal error has occurred' }).end();
+            }
+        );
+    });
+});
+
+module.exports = stateApp;
diff --git a/src/server.js b/src/server.js
new file mode 100644
index 00000000..7b3927e3
--- /dev/null
+++ b/src/server.js
@@ -0,0 +1,16 @@
+const debug = require('debug')('node-express-base');
+
+const libs = `${process.cwd()}/libs`;
+
+const config = require(`${libs}/config`);
+
+const log = require(`${libs}/log`)(module);
+
+const app = require(`${libs}/app`);
+
+app.set('port', config.get('port') || 3000);
+
+const server = app.listen(app.get('port'), () => {
+    debug(`Express server listening on port ${server.address().port}`);
+    log.info(`Express server listening on port ${config.get('port')}`);
+});
diff --git a/src/test/test.js b/src/test/test.js
new file mode 100644
index 00000000..f9fec313
--- /dev/null
+++ b/src/test/test.js
@@ -0,0 +1,155 @@
+const chai = require('chai');
+const chaiHttp = require('chai-http');
+const assert = chai.assert;
+const expect = chai.expect;
+const should = chai.should(); // actually call the function
+const server = require('../libs/app');
+
+chai.use(chaiHttp);
+
+describe('request enrollments', () => {
+    it('should list enrollments', (done) => {
+        chai.request(server)
+            .get('/v1/enrollments')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('name');
+                res.body.result[0].should.have.property('total');
+                done();
+            });
+    });
+});
+
+describe('request regions', () => {
+    it('should list all regions', (done) => {
+        chai.request(server)
+            .get('/v1/regions')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+
+    it('should list region by id', (done) => {
+        chai.request(server)
+            .get('/v1/regions/1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result.should.have.length(1);
+                res.body.result[0].should.have.property('pk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+});
+
+describe('request states', () => {
+
+    it('should list all states', (done) => {
+        chai.request(server)
+            .get('/v1/states')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_estado_id');
+                res.body.result[0].should.have.property('fk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+
+    it('should list a state by id', (done) => {
+        chai.request(server)
+            .get('/v1/states/11')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result.should.have.length(1);
+                res.body.result[0].should.have.property('pk_estado_id');
+                res.body.result[0].should.have.property('fk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+
+    it('should list states by region id', (done) => {
+        chai.request(server)
+            .get('/v1/states/region/1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_estado_id');
+                res.body.result[0].should.have.property('fk_regiao_id');
+                res.body.result[0].should.have.property('nome');
+                done();
+            });
+    });
+});
+
+describe('request cities', () => {
+
+    it('should list all cities', (done) => {
+        chai.request(server)
+            .get('/v1/cities')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_municipio_id');
+                res.body.result[0].should.have.property('fk_estado_id');
+                res.body.result[0].should.have.property('nome');
+                res.body.result[0].should.have.property('codigo_ibge');
+                done();
+            });
+    });
+
+    it('should list a city by id', (done) => {
+        chai.request(server)
+            .get('/v1/cities/1')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_municipio_id');
+                res.body.result[0].should.have.property('fk_estado_id');
+                res.body.result[0].should.have.property('nome');
+                res.body.result[0].should.have.property('codigo_ibge');
+                done();
+            });
+    });
+
+    it('should list a city by codigo_ibge', (done) => {
+        chai.request(server)
+            .get('/v1/cities/ibge/1200013')
+            .end((err, res) => {
+                res.should.have.status(200);
+                res.should.be.json;
+                res.body.should.have.property('result');
+                res.body.result.should.be.a('array');
+                res.body.result[0].should.have.property('pk_municipio_id');
+                res.body.result[0].should.have.property('fk_estado_id');
+                res.body.result[0].should.have.property('nome');
+                res.body.result[0].should.have.property('codigo_ibge');
+                done();
+            });
+    });
+});
diff --git a/test/test.js b/test/test.js
deleted file mode 100644
index a9b67bb2..00000000
--- a/test/test.js
+++ /dev/null
@@ -1,158 +0,0 @@
-var chai = require('chai');
-var chaiHttp = require('chai-http');
-var assert = chai.assert;
-var expect = chai.expect;
-var should = chai.should(); //actually call the function
-var server = require('../libs/app');
-
-chai.use(chaiHttp);
-
-describe('request enrollments', function(){
-
-  it('should list enrollments', function(done){
-    chai.request(server)
-      .get('/v1/enrollments')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('name');
-        res.body.result[0].should.have.property('total');
-        done();
-      })
-  });
-});
-
-describe('request regions', function(){
-
-  it('should list all regions', function(done){
-    chai.request(server)
-      .get('/v1/regions')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-
-  it('should list region by id', function(done){
-    chai.request(server)
-      .get('/v1/regions/1')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result.should.have.length(1);
-        res.body.result[0].should.have.property('pk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-});
-
-describe('request states', function(){
-
-  it('should list all states', function(done){
-    chai.request(server)
-      .get('/v1/states')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_estado_id');
-        res.body.result[0].should.have.property('fk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-
-  it('should list a state by id', function(done){
-    chai.request(server)
-      .get('/v1/states/11')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result.should.have.length(1);
-        res.body.result[0].should.have.property('pk_estado_id');
-        res.body.result[0].should.have.property('fk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-
-  it('should list states by region id', function(done){
-    chai.request(server)
-      .get('/v1/states/region/1')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_estado_id');
-        res.body.result[0].should.have.property('fk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-});
-
-describe('request cities', function(){
-
-  it('should list all cities', function(done){
-    chai.request(server)
-      .get('/v1/cities')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_municipio_id');
-        res.body.result[0].should.have.property('fk_estado_id');
-        res.body.result[0].should.have.property('nome');
-        res.body.result[0].should.have.property('codigo_ibge');
-        done();
-      })
-  });
-
-  it('should list a city by id', function(done){
-    chai.request(server)
-      .get('/v1/cities/1')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_municipio_id');
-        res.body.result[0].should.have.property('fk_estado_id');
-        res.body.result[0].should.have.property('nome');
-        res.body.result[0].should.have.property('codigo_ibge');
-        done();
-      })
-  });
-
-  it('should list a city by codigo_ibge', function(done){
-    chai.request(server)
-      .get('/v1/cities/ibge/1200013')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_municipio_id');
-        res.body.result[0].should.have.property('fk_estado_id');
-        res.body.result[0].should.have.property('nome');
-        res.body.result[0].should.have.property('codigo_ibge');
-        done();
-      })
-  });
-
-});
-- 
GitLab


From 2e3dc702bc9d7b9b7549e8c0fb4d9374cfd0dc88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 17 Aug 2016 20:05:34 -0300
Subject: [PATCH 07/58] Add installing the packages globally in .gitlab-ci.yml

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

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a3c44c8f..d75a36f5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,6 +2,7 @@ stages:
   - test
 
 before_script:
+  - sudo npm install -g
   - npm install
 
 run_tests:
-- 
GitLab


From 53dfb7a1f722a53628eb0671289f5f9aa3dda559 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 17 Aug 2016 20:18:34 -0300
Subject: [PATCH 08/58] Change installing gulp via apt-get command for npm

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

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d75a36f5..765eff8b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,8 +2,8 @@ stages:
   - test
 
 before_script:
-  - sudo npm install -g
   - npm install
+  - npm install --global gulp-cli
 
 run_tests:
   stage: test
-- 
GitLab


From 30fda4edd1105c8807a68e1343afc90d0cf88d98 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 17 Aug 2016 20:21:29 -0300
Subject: [PATCH 09/58] Add mocha to .gitlab-ci.yml
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Risso <jvtr12@c3sl.ufpr.br>
---
 .gitlab-ci.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 765eff8b..1a6d95ee 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -4,6 +4,7 @@ stages:
 before_script:
   - npm install
   - npm install --global gulp-cli
+  - npm install --global mocha
 
 run_tests:
   stage: test
-- 
GitLab


From 9067ca138e3d03275e0eb8ed5697ed23ebbdc846 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 17 Aug 2016 20:24:40 -0300
Subject: [PATCH 10/58] Add gulp-cli to packages.json

---
 package.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/package.json b/package.json
index cee6b8e0..141451dd 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
     "eslint-plugin-react": "^6.1.1",
     "gulp": "^3.9.1",
     "gulp-babel": "^6.1.2",
+    "gulp-cli": "^1.2.2",
     "gulp-eslint": "^3.0.1",
     "mocha": "^2.5.3"
   }
-- 
GitLab


From a7f9345b60b575019a9f29ad14459c1fba8cace8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 17 Aug 2016 20:24:57 -0300
Subject: [PATCH 11/58] Fix database server address in build/config.json
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Risso <jvtr12@c3sl.ufpr.br>
---
 build/config.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/build/config.json b/build/config.json
index c919cf1c..076d753b 100644
--- a/build/config.json
+++ b/build/config.json
@@ -1,7 +1,7 @@
 {
     "port": 3000,
     "monetdb": {
-        "host": "localhost",
+        "host": "simcaqdb1",
         "port": 50000,
         "dbname": "simcaq_dev",
         "user": "monetdb",
-- 
GitLab


From 029f378bdb0c85e7de35b325ca50d54ab8b19adc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Wed, 17 Aug 2016 20:33:42 -0300
Subject: [PATCH 12/58] Fix city query by IBGE code route
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Risso <jvtr12@c3sl.ufpr.br>
---
 build/libs/routes/cities.js | 4 ++--
 src/libs/routes/cities.js   | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/build/libs/routes/cities.js b/build/libs/routes/cities.js
index f549624e..6a0048f2 100644
--- a/build/libs/routes/cities.js
+++ b/build/libs/routes/cities.js
@@ -52,9 +52,9 @@ cityApp.get('/:id', function (req, res) {
 
 cityApp.get('/ibge/:id', function (req, res) {
     var citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
-    var cityId = parseInt(req.params.id, 10);
+    var cityIbgeCode = req.params.id;
     conn.prepare(citySql, true).then(function (dbQuery) {
-        dbQuery.exec([cityId]).then(function (dbResult) {
+        dbQuery.exec([cityIbgeCode]).then(function (dbResult) {
             log.debug(dbResult);
             req.result = dbResult;
             response(req, res);
diff --git a/src/libs/routes/cities.js b/src/libs/routes/cities.js
index 1ae6c48d..07e91477 100644
--- a/src/libs/routes/cities.js
+++ b/src/libs/routes/cities.js
@@ -56,9 +56,9 @@ cityApp.get('/:id', (req, res) => {
 
 cityApp.get('/ibge/:id', (req, res) => {
     const citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
-    const cityId = parseInt(req.params.id, 10);
+    const cityIbgeCode = req.params.id;
     conn.prepare(citySql, true).then((dbQuery) => {
-        dbQuery.exec([cityId]).then(
+        dbQuery.exec([cityIbgeCode]).then(
             (dbResult) => {
                 log.debug(dbResult);
                 req.result = dbResult;
-- 
GitLab


From ed91f01eeef52f21f28c887c611002b7da853d4b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Mon, 29 Aug 2016 13:53:45 -0300
Subject: [PATCH 13/58] Refactor source structure and add promise to execute DB
 queries

Changes in this commit:
- Singularize all routes and components
- Separate different modules into their own source files
- Rename the API module to enrollment and add a more general API module
- Resources that are related to a route, are now under its scope. For
  example, the year range of the enrollment component is now under the
  route /v1/enrollment/year_range
- Implement a thenable function to execute the queries against the
  database to reduce the amount of duplicated code in the application. This
  approach also allows several concurrent queries to run concurrently in the
  same route.
---
 config.json                    |   3 +-
 src/libs/app.js                |  13 +-
 src/libs/db/query_decorator.js |  25 ---
 src/libs/db/query_exec.js      |  42 ++++
 src/libs/routes/api.js         | 339 ++-------------------------------
 src/libs/routes/cities.js      |  95 ---------
 src/libs/routes/city.js        |  70 +++++++
 src/libs/routes/enrollment.js  | 305 +++++++++++++++++++++++++++++
 src/libs/routes/region.js      |  46 +++++
 src/libs/routes/regions.js     |  58 ------
 src/libs/routes/state.js       |  58 ++++++
 src/libs/routes/states.js      |  77 --------
 12 files changed, 538 insertions(+), 593 deletions(-)
 delete mode 100644 src/libs/db/query_decorator.js
 create mode 100644 src/libs/db/query_exec.js
 delete mode 100644 src/libs/routes/cities.js
 create mode 100644 src/libs/routes/city.js
 create mode 100644 src/libs/routes/enrollment.js
 create mode 100644 src/libs/routes/region.js
 delete mode 100644 src/libs/routes/regions.js
 create mode 100644 src/libs/routes/state.js
 delete mode 100644 src/libs/routes/states.js

diff --git a/config.json b/config.json
index 1da8951e..21862d34 100644
--- a/config.json
+++ b/config.json
@@ -5,7 +5,8 @@
         "port": 50000,
         "dbname": "simcaq_dev",
         "user": "monetdb",
-        "password":"monetdb"
+        "password":"monetdb",
+        "nrConnections": "16"
     },
     "default": {
         "api": {
diff --git a/src/libs/app.js b/src/libs/app.js
index c2396420..07d7d28f 100644
--- a/src/libs/app.js
+++ b/src/libs/app.js
@@ -6,23 +6,16 @@ const cors = require('cors');
 
 const log = require('./log')(module);
 
-const api = require('./routes/api');
-const states = require('./routes/states');
-const regions = require('./routes/regions');
-const cities = require('./routes/cities');
-
 const app = express();
 
+const api = require('./routes/api');
+
 app.use(bodyParser.json());
 app.use(bodyParser.urlencoded({ extended: false }));
 app.use(cookieParser());
 app.use(cors());
 app.use(methodOverride());
-
-app.use('/v1/', api);
-app.use('/v1/states', states);
-app.use('/v1/regions', regions);
-app.use('/v1/cities', cities);
+app.use(api);
 
 // catch 404 and forward to error handler
 app.use((req, res, next) => {
diff --git a/src/libs/db/query_decorator.js b/src/libs/db/query_decorator.js
deleted file mode 100644
index 361f3509..00000000
--- a/src/libs/db/query_decorator.js
+++ /dev/null
@@ -1,25 +0,0 @@
-const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
-const conn = require(`${libs}/db/monet`);
-
-/**
- * Basic decorator to wrap SQL query strings
- */
-exports.execQuery = (sqlQuery, sqlQueryParams) => {
-    log.debug(`Executing SQL query '${sqlQuery}' with params '${sqlQueryParams}'`);
-    conn.prepare(sqlQuery, true).then(
-        (dbQuery) => {
-            dbQuery.exec(sqlQueryParams).then(
-                (dbResult) => {
-                    log.debug(dbResult.data);
-                    log.debug(`Query result: ${dbResult.data}`);
-                    return dbResult;
-                },
-                (error) => {
-                    log.error(`SQL query execution error: ${error.message}`);
-                    return error;
-                }
-            );
-        }
-    );
-};
diff --git a/src/libs/db/query_exec.js b/src/libs/db/query_exec.js
new file mode 100644
index 00000000..c1464f28
--- /dev/null
+++ b/src/libs/db/query_exec.js
@@ -0,0 +1,42 @@
+const libs = `${process.cwd()}/libs`;
+const log = require(`${libs}/log`)(module);
+const conn = require(`${libs}/db/monet`);
+
+/**
+ * Promise that executes an SQL query with optional parameters
+ *
+ * Examples:
+ *     Query with no parameters:
+ *         execSqlQuery('SELECT * FROM people');
+ *     Query with one parameter:
+ *         execSqlQuery('SELECT name, age FROM people WHERE id = ?', [1]);
+ *     Query with more than one parameter:
+ *         execSqlQuery('SELECT name, age FROM people WHERE city = ? AND age > ?', ['São Paulo', 35]);
+ *
+ * @param {string} sqlQuery       - SQL query to be executed by the Promise
+ * @param {array}  sqlQueryParams - SQL query parameters
+ */
+function execSqlQuery(sqlQuery, sqlQueryParams = []) {
+    log.debug(`Executing SQL query '${sqlQuery}' with params '${sqlQueryParams}'`);
+    return new Promise((resolve, reject) => {
+        conn.prepare(sqlQuery, true).then(
+            (dbQuery) => {
+                dbQuery.exec(sqlQueryParams).then(
+                    (dbResult) => {
+                        log.debug(`Query result: ${dbResult.data}`);
+                        log.debug(dbResult.data);
+                        resolve(dbResult.data);
+                    },
+                    (dbError) => {
+                        log.error(`SQL query execution error: ${error.message}`);
+                        reject(new Error(dbError.message));
+                    }
+                );
+                // release resources allocated for prepared statement
+                conn.release();
+            }
+        );
+    });
+}
+
+module.exports = execSqlQuery;
diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js
index d5c46ef6..29fe4564 100644
--- a/src/libs/routes/api.js
+++ b/src/libs/routes/api.js
@@ -1,333 +1,18 @@
 const express = require('express');
+const api = express();
+const enrollment = require('./enrollment');
+const state = require('./state');
+const region = require('./region');
+const city = require('./city');
 
-const xml = require('js2xmlparser');
-
-const enrollmentApp = express();
-
-const libs = `${process.cwd()}/libs`;
-
-const log = require(`${libs}/log`)(module);
-
-const conn = require(`${libs}/db/monet`);
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ state: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
-
-enrollmentApp.get('/', (req, res) => {
+api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API is running' });
 });
 
-/**
- * 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) => {
-    const yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)'
-        + 'AS end_year FROM turmas AS t';
-
-    conn.query(yearSql, true).then(
-        (dbResult) => {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        },
-        (dbError) => {
-            log.error(`SQL query execution error: ${dbError.message}`);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        }
-    );
-});
-
-enrollmentApp.get('/education_level', (req, res) => {
-    const edLevelSql = 'SELECT pk_etapa_ensino_id AS id, desc_etapa AS '
-        + 'education_level FROM etapa_ensino';
-    conn.query(edLevelSql, true).then(
-        (dbResult) => {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        },
-        (dbError) => {
-            log.error(`SQL query error: ${dbError.message}`);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        }
-    );
-});
-
-enrollmentApp.get('/data', (req, res) => {
-    log.debug(req.query);
-    log.debug(req.query.met);
-    log.debug(req.query.dim);
-    const schoolClassSql = 'SELECT * FROM turmas';
-    conn.query(schoolClassSql, true).then(
-        (dbResult) => {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        },
-        (dbError) => {
-            log.error(`SQL query error: ${dbError.message}`);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        }
-    );
-});
-
-enrollmentApp.use('/enrollments', (req, res, next) => {
-    const params = req.query;
-    req.paramCnt = 0;
-
-    if (typeof params.id !== 'undefined') {
-        req.id = parseInt(params.id, 10);
-        req.paramCnt += 1;
-    }
-
-    if (typeof params.location_id !== 'undefined') {
-        req.location_id = parseInt(params.location_id, 10);
-        req.paramCnt += 1;
-    }
-
-    if (typeof params.adm_dependency_id !== 'undefined') {
-        req.adm_dependency_id = parseInt(params.adm_dependency_id, 10);
-        req.paramCnt += 1;
-    }
-
-    if (typeof params.census_year !== 'undefined') {
-        req.census_year = parseInt(params.census_year, 10);
-        req.paramCnt += 1;
-    }
-
-    if (typeof params.education_level_id !== 'undefined') {
-        req.education_level_id = parseInt(params.education_level_id, 10);
-        req.paramCnt += 1;
-    }
-
-    next();
-});
-
-enrollmentApp.use('/enrollments', (req, res, next) => {
-    const params = req.query;
-    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
-        log.debug('Using enrollments query for regions');
-        req.sqlQuery = 'SELECT r.nome AS name, COALESCE(SUM(t.num_matriculas), 0) AS total '
-            + 'FROM regioes AS r '
-            + 'INNER JOIN estados AS e ON r.pk_regiao_id = e.fk_regiao_id '
-            + 'INNER JOIN municipios AS m ON e.pk_estado_id = m.fk_estado_id '
-            + 'LEFT OUTER JOIN turmas AS t ON ( '
-            + 'm.pk_municipio_id = t.fk_municipio_id ';
-        req.sqlQueryParams = [];
-
-        if (typeof req.census_year !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.ano_censo = ?';
-            req.sqlQueryParams.push(req.census_year);
-        }
-
-        if (typeof req.adm_dependency_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
-            req.sqlQueryParams.push(req.adm_dependency_id);
-        }
-
-        if (typeof req.location_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.id_localizacao = ?';
-            req.sqlQueryParams.push(req.location_id);
-        }
-
-        if (typeof req.education_level_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
-            req.sqlQueryParams.push(req.education_level_id);
-        }
-
-        req.sqlQuery += ')';
-        if (typeof req.id !== 'undefined') {
-            req.sqlQuery += ' WHERE ';
-            req.sqlQuery += 'r.pk_regiao_id = ?';
-            req.sqlQueryParams.push(req.id);
-        }
-        req.sqlQuery += ' GROUP BY r.nome';
-    }
-    next();
-});
-
-enrollmentApp.use('/enrollments', (req, res, next) => {
-    const params = req.query;
-    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
-        log.debug('Using enrollments query for states');
-        req.sqlQuery = 'SELECT e.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total '
-            + 'FROM estados AS e '
-            + 'INNER JOIN municipios AS m ON m.fk_estado_id = e.pk_estado_id '
-            + 'LEFT OUTER JOIN turmas AS t ON ('
-            + 'm.pk_municipio_id = t.fk_municipio_id ';
-        req.sqlQueryParams = [];
-
-        if (typeof req.census_year !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.ano_censo = ?';
-            req.sqlQueryParams.push(req.census_year);
-        }
-
-        if (typeof req.adm_dependency_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
-            req.sqlQueryParams.push(req.adm_dependency_id);
-        }
-
-        if (typeof req.location_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.id_localizacao = ?';
-            req.sqlQueryParams.push(req.location_id);
-        }
-
-        if (typeof req.education_level_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
-            req.sqlQueryParams.push(req.education_level_id);
-        }
-
-        req.sqlQuery += ')';
-
-        if (typeof req.id !== 'undefined') {
-            req.sqlQuery += ' WHERE ';
-            req.sqlQuery += 'e.pk_estado_id = ?';
-            req.sqlQueryParams.push(req.id);
-        }
-
-        req.sqlQuery += ' GROUP BY e.nome';
-    }
-    next();
-});
-
-enrollmentApp.use('/enrollments', (req, res, next) => {
-    const params = req.query;
-    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
-        log.debug('Using enrollments query for cities');
-        req.sqlQuery = 'SELECT m.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total '
-            + 'FROM municipios AS m '
-            + 'LEFT OUTER JOIN turmas AS t ON ( '
-            + 'm.pk_municipio_id = t.fk_municipio_id';
-        req.sqlQueryParams = [];
-
-        if (typeof req.census_year !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.ano_censo = ?';
-            req.sqlQueryParams.push(req.census_year);
-        }
-
-        if (typeof req.adm_dependency_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
-            req.sqlQueryParams.push(req.adm_dependency_id);
-        }
-
-        if (typeof req.location_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.id_localizacao = ?';
-            req.sqlQueryParams.push(req.location_id);
-        }
-
-        if (typeof req.education_level_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
-            req.sqlQueryParams.push(req.education_level_id);
-        }
-
-        req.sqlQuery += ')';
-
-        if (typeof req.id !== 'undefined') {
-            req.sqlQuery += ' WHERE ';
-            req.sqlQuery += 'm.pk_municipio_id = ?';
-            req.sqlQueryParams.push(req.id);
-        }
-
-        req.sqlQuery += 'GROUP BY m.nome';
-    }
-    next();
-});
-
-enrollmentApp.use('/enrollments', (req, res, next) => {
-    const params = req.query;
-    if (typeof params.aggregate === 'undefined') {
-        log.debug('Using enrollments query for the whole country');
-        req.sqlQuery = 'SELECT \'Brasil\' AS name, COALESCE(SUM(t.num_matriculas),0) AS total '
-            + 'FROM turmas AS t';
-        req.sqlQueryParams = [];
-
-        if (req.paramCnt > 0) {
-            req.sqlQuery += ' WHERE ';
-        }
-
-        if (typeof req.census_year !== 'undefined') {
-            req.sqlQuery += 't.ano_censo = ?';
-            req.sqlQueryParams.push(req.census_year);
-        }
-
-        if (typeof req.adm_dependency_id !== 'undefined') {
-            if (req.sqlQueryParams.length > 0) {
-                req.sqlQuery += ' AND ';
-            }
-            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
-            req.sqlQueryParams.push(req.adm_dependency_id);
-        }
-
-        if (typeof req.location_id !== 'undefined') {
-            if (req.sqlQueryParams.length > 0) {
-                req.sqlQuery += ' AND ';
-            }
-            req.sqlQuery += 't.id_localizacao = ?';
-            req.sqlQueryParams.push(req.location_id);
-        }
-
-        if (typeof req.education_level_id !== 'undefined') {
-            if (req.sqlQueryParams.length > 0) {
-                req.sqlQuery += ' AND ';
-            }
-            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
-            req.sqlQueryParams.push(req.education_level_id);
-        }
-    }
-    next();
-});
-
-enrollmentApp.get('/enrollments', (req, res, next) => {
-    log.debug(`Request parameters: ${req}`);
-    if (typeof req.sqlQuery === 'undefined') {
-        // Should only happen if there is a bug in the chaining of the
-        // '/enrollments' route, since when no +aggregate+ parameter is given,
-        // it defaults to use the query for the whole country.
-        log.error('BUG -- No SQL query was found to be executed!');
-        res.send('Request could not be satisfied due to an internal error');
-    } else {
-        log.debug('SQL query: ${ req.sqlQuery }?');
-        log.debug(req.sqlQuery);
-
-        conn.prepare(req.sqlQuery, true).then((dbQuery) => {
-            dbQuery.exec(req.sqlQueryParams).then(
-                (dbResult) => {
-                    log.debug(dbResult);
-                    req.result = dbResult;
-                    response(req, res);
-                },
-                (dbError) => {
-                    log.error(`SQL query execution error: ${dbError.message}`);
-                    // FIXME: change response to HTTP 501 status
-                    res.json({ error: 'An internal error has occurred' }).end();
-                }
-            );
-        });
-    }
-});
+// mount API routes
+api.use('/v1/enrollment', enrollment);
+api.use('/v1/state',      state);
+api.use('/v1/region',     region);
+api.use('/v1/city',       city);
 
-module.exports = enrollmentApp;
+module.exports = api;
diff --git a/src/libs/routes/cities.js b/src/libs/routes/cities.js
deleted file mode 100644
index 07e91477..00000000
--- a/src/libs/routes/cities.js
+++ /dev/null
@@ -1,95 +0,0 @@
-const express = require('express');
-
-const xml = require('js2xmlparser');
-
-const cityApp = express();
-
-const libs = `${process.cwd()}/libs`;
-
-const log = require(`${libs}/log`)(module);
-
-const conn = require(`${libs}/db/monet`);
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ city: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
-
-cityApp.get('/', (req, res) => {
-    conn.query('SELECT * FROM municipios', true).then(
-        (dbResult) => {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        },
-        (dbError) => {
-            log.error(`SQL query execution error: ${dbError.message}`);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        }
-    );
-});
-
-cityApp.get('/:id', (req, res) => {
-    const citySql = 'SELECT * FROM municipios WHERE pk_municipio_id = ?';
-    const cityId = parseInt(req.params.id, 10);
-    conn.prepare(citySql, true).then((dbQuery) => {
-        dbQuery.exec([cityId]).then(
-            (dbResult) => {
-                log.debug(dbResult);
-                req.result = dbResult;
-                response(req, res);
-            },
-            (dbError) => {
-                log.error(`SQL query execution error: ${dbError.message}`);
-                // FIXME: change response to HTTP 501 status
-                res.json({ error: 'An internal error has occurred' }).end();
-            }
-        );
-    });
-});
-
-cityApp.get('/ibge/:id', (req, res) => {
-    const citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
-    const cityIbgeCode = req.params.id;
-    conn.prepare(citySql, true).then((dbQuery) => {
-        dbQuery.exec([cityIbgeCode]).then(
-            (dbResult) => {
-                log.debug(dbResult);
-                req.result = dbResult;
-                response(req, res);
-            },
-            (dbError) => {
-                log.error(`SQL query execution error: ${dbError.message}`);
-                // FIXME: change response to HTTP 501 status
-                res.json({ error: 'An internal error has occurred' }).end();
-            }
-        );
-    });
-});
-
-cityApp.get('/state/:id', (req, res) => {
-    const citySql = 'SELECT * FROM municipios WHERE fk_estado_id = ?';
-    const stateId = parseInt(req.params.id, 10);
-    conn.prepare(citySql, true).then((dbQuery) => {
-        dbQuery.exec([stateId]).then(
-            (dbResult) => {
-                log.debug(dbResult);
-                req.result = dbResult;
-                response(req, res);
-            },
-            (dbError) => {
-                log.error(`SQL query execution error: ${dbError.message}`);
-                // FIXME: change response to HTTP 501 status
-                res.json({ error: 'An internal error has occurred' }).end();
-            }
-        );
-    });
-});
-
-module.exports = cityApp;
diff --git a/src/libs/routes/city.js b/src/libs/routes/city.js
new file mode 100644
index 00000000..c7deb75f
--- /dev/null
+++ b/src/libs/routes/city.js
@@ -0,0 +1,70 @@
+const express = require('express');
+
+const xml = require('js2xmlparser');
+
+const cityApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const dbQuery = require(`${libs}/db/query_exec`);
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ city: req.result })));
+    } else {
+        res.json({ result: req.result });
+    }
+}
+
+cityApp.get('/', (req, res) => {
+    const citySql = 'SELECT * FROM municipios';
+    dbQuery(yearSql).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+cityApp.get('/:id', (req, res) => {
+    const citySql = 'SELECT * FROM municipios WHERE pk_municipio_id = ?';
+    const cityId = parseInt(req.params.id, 10);
+    dbQuery(citySql, [cityId]).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+cityApp.get('/ibge/:id', (req, res) => {
+    const citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
+    const cityIbgeCode = req.params.id;
+    dbQuery(citySql, [cityIbgeCode]).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+cityApp.get('/state/:id', (req, res) => {
+    const citySql = 'SELECT * FROM municipios WHERE fk_estado_id = ?';
+    const stateId = parseInt(req.params.id, 10);
+    dbQuery(citySql, [stateId]).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+module.exports = cityApp;
diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
new file mode 100644
index 00000000..131ab7ae
--- /dev/null
+++ b/src/libs/routes/enrollment.js
@@ -0,0 +1,305 @@
+const express = require('express');
+
+const xml = require('js2xmlparser');
+
+const enrollmentApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const dbQuery = require('../db/query_exec');
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        return res.csv(req.result);
+    } else if (req.query.format === 'xml') {
+        return res.send(xml('result', JSON.stringify({ state: req.result })));
+    } else {
+        return res.json({ result: req.result });
+    }
+}
+/**
+ * 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) => {
+    const yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)'
+        + 'AS end_year FROM turmas AS t';
+
+    dbQuery(yearSql).then((result) => {
+        const record = result['0'];
+        log.debug(record);
+        req.result = { start_year: record.start_year, end_year: record.end_year };
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+enrollmentApp.get('/education_level', (req, res) => {
+    const edLevelSql = 'SELECT pk_etapa_ensino_id AS id, desc_etapa AS '
+        + 'education_level FROM etapa_ensino';
+
+    dbQuery(edLevelSql).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+enrollmentApp.get('/data', (req, res) => {
+    const schoolClassSql = 'SELECT * FROM turmas';
+    dbQuery(schoolClassSql).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+enrollmentApp.use('/enrollments', (req, res, next) => {
+    const params = req.query;
+    req.paramCnt = 0;
+
+    if (typeof params.id !== 'undefined') {
+        req.id = parseInt(params.id, 10);
+        req.paramCnt += 1;
+    }
+
+    if (typeof params.location_id !== 'undefined') {
+        req.location_id = parseInt(params.location_id, 10);
+        req.paramCnt += 1;
+    }
+
+    if (typeof params.adm_dependency_id !== 'undefined') {
+        req.adm_dependency_id = parseInt(params.adm_dependency_id, 10);
+        req.paramCnt += 1;
+    }
+
+    if (typeof params.census_year !== 'undefined') {
+        req.census_year = parseInt(params.census_year, 10);
+        req.paramCnt += 1;
+    }
+
+    if (typeof params.education_level_id !== 'undefined') {
+        req.education_level_id = parseInt(params.education_level_id, 10);
+        req.paramCnt += 1;
+    }
+
+    next();
+});
+
+enrollmentApp.use('/enrollments', (req, res, next) => {
+    const params = req.query;
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
+        log.debug('Using enrollments query for regions');
+        req.sqlQuery = 'SELECT r.nome AS name, COALESCE(SUM(t.num_matriculas), 0) AS total '
+            + 'FROM regioes AS r '
+            + 'INNER JOIN estados AS e ON r.pk_regiao_id = e.fk_regiao_id '
+            + 'INNER JOIN municipios AS m ON e.pk_estado_id = m.fk_estado_id '
+            + 'LEFT OUTER JOIN turmas AS t ON ( '
+            + 'm.pk_municipio_id = t.fk_municipio_id ';
+        req.sqlQueryParams = [];
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
+        req.sqlQuery += ')';
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'r.pk_regiao_id = ?';
+            req.sqlQueryParams.push(req.id);
+        }
+        req.sqlQuery += ' GROUP BY r.nome';
+    }
+    next();
+});
+
+enrollmentApp.use('/enrollments', (req, res, next) => {
+    const params = req.query;
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
+        log.debug('Using enrollments query for states');
+        req.sqlQuery = 'SELECT e.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total '
+            + 'FROM estados AS e '
+            + 'INNER JOIN municipios AS m ON m.fk_estado_id = e.pk_estado_id '
+            + 'LEFT OUTER JOIN turmas AS t ON ('
+            + 'm.pk_municipio_id = t.fk_municipio_id ';
+        req.sqlQueryParams = [];
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
+        req.sqlQuery += ')';
+
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'e.pk_estado_id = ?';
+            req.sqlQueryParams.push(req.id);
+        }
+
+        req.sqlQuery += ' GROUP BY e.nome';
+    }
+    next();
+});
+
+enrollmentApp.use('/enrollments', (req, res, next) => {
+    const params = req.query;
+    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
+        log.debug('Using enrollments query for cities');
+        req.sqlQuery = 'SELECT m.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total '
+            + 'FROM municipios AS m '
+            + 'LEFT OUTER JOIN turmas AS t ON ( '
+            + 'm.pk_municipio_id = t.fk_municipio_id';
+        req.sqlQueryParams = [];
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            req.sqlQuery += ' AND ';
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+
+        req.sqlQuery += ')';
+
+        if (typeof req.id !== 'undefined') {
+            req.sqlQuery += ' WHERE ';
+            req.sqlQuery += 'm.pk_municipio_id = ?';
+            req.sqlQueryParams.push(req.id);
+        }
+
+        req.sqlQuery += 'GROUP BY m.nome';
+    }
+    next();
+});
+
+enrollmentApp.use('/enrollments', (req, res, next) => {
+    const params = req.query;
+    if (typeof params.aggregate === 'undefined') {
+        log.debug('Using enrollments query for the whole country');
+        req.sqlQuery = 'SELECT \'Brasil\' AS name, COALESCE(SUM(t.num_matriculas),0) AS total '
+            + 'FROM turmas AS t';
+        req.sqlQueryParams = [];
+
+        if (req.paramCnt > 0) {
+            req.sqlQuery += ' WHERE ';
+        }
+
+        if (typeof req.census_year !== 'undefined') {
+            req.sqlQuery += 't.ano_censo = ?';
+            req.sqlQueryParams.push(req.census_year);
+        }
+
+        if (typeof req.adm_dependency_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
+            req.sqlQueryParams.push(req.adm_dependency_id);
+        }
+
+        if (typeof req.location_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.id_localizacao = ?';
+            req.sqlQueryParams.push(req.location_id);
+        }
+
+        if (typeof req.education_level_id !== 'undefined') {
+            if (req.sqlQueryParams.length > 0) {
+                req.sqlQuery += ' AND ';
+            }
+            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
+            req.sqlQueryParams.push(req.education_level_id);
+        }
+    }
+    next();
+});
+
+enrollmentApp.get('/enrollments', (req, res, next) => {
+    log.debug(`Request parameters: ${req}`);
+    if (typeof req.sqlQuery === 'undefined') {
+        // Should only happen if there is a bug in the chaining of the
+        // '/enrollments' route, since when no +aggregate+ parameter is given,
+        // it defaults to use the query for the whole country.
+        log.error('BUG -- No SQL query was found to be executed!');
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    } else {
+        log.debug('SQL query: ${ req.sqlQuery }?');
+        log.debug(req.sqlQuery);
+        dbQuery(req.sqlQuery).then((result) => {
+            req.result = result;
+            return response(req, res);
+        }, (error) => {
+            log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+            next('Internal error, request could not be satisfied at this moment. Please, try again later');
+        });
+    }
+});
+
+module.exports = enrollmentApp;
diff --git a/src/libs/routes/region.js b/src/libs/routes/region.js
new file mode 100644
index 00000000..36b1f049
--- /dev/null
+++ b/src/libs/routes/region.js
@@ -0,0 +1,46 @@
+const express = require('express');
+
+const xml = require('js2xmlparser');
+
+const regionApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const dbQuery = require(`${libs}/db/query_exec`);
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ state: req.result })));
+    } else {
+        res.json({ result: req.result });
+    }
+}
+
+regionApp.get('/', (req, res) => {
+    const regionSql = 'SELECT * FROM regioes';
+    dbQuery(regionSql).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+regionApp.get('/:id', (req, res) => {
+    const regionSql = 'SELECT * FROM regioes WHERE pk_regiao_id = ?';
+    const regionId = parseInt(req.params.id, 10);
+    dbQuery(regionSql, [regionId]).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+module.exports = regionApp;
diff --git a/src/libs/routes/regions.js b/src/libs/routes/regions.js
deleted file mode 100644
index d76037c0..00000000
--- a/src/libs/routes/regions.js
+++ /dev/null
@@ -1,58 +0,0 @@
-const express = require('express');
-
-const xml = require('js2xmlparser');
-
-const regionApp = express();
-
-const libs = `${process.cwd()}/libs`;
-
-const log = require(`${libs}/log`)(module);
-
-const conn = require(`${libs}/db/monet`);
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ state: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
-
-regionApp.get('/', (req, res) => {
-    const regionSql = 'SELECT * FROM regioes';
-    conn.query(regionSql, true).then(
-        (dbResult) => {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        },
-        (dbError) => {
-            log.error(`SQL query execution error: ${dbError.message}`);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        }
-    );
-});
-
-regionApp.get('/:id', (req, res) => {
-    const regionSql = 'SELECT * FROM regioes WHERE pk_regiao_id = ?';
-    const regionId = parseInt(req.params.id, 10);
-    conn.prepare(regionSql, true).then((dbQuery) => {
-        dbQuery.exec([regionId]).then(
-            (dbResult) => {
-                log.debug(dbResult);
-                req.result = dbResult;
-                response(req, res);
-            },
-            (dbError) => {
-                log.error(`SQL query execution error: ${dbError.message}`);
-                // FIXME: change response to HTTP 501 status
-                res.json({ error: 'An internal error has occurred' }).end();
-            }
-        );
-    });
-});
-
-module.exports = regionApp;
diff --git a/src/libs/routes/state.js b/src/libs/routes/state.js
new file mode 100644
index 00000000..a1fa64a5
--- /dev/null
+++ b/src/libs/routes/state.js
@@ -0,0 +1,58 @@
+const express = require('express');
+
+const xml = require('js2xmlparser');
+
+const stateApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const dbQuery = require(`${libs}/db/query_exec`);
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result.data);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ state: req.result.data })));
+    } else {
+        res.json({ result: req.result.data });
+    }
+}
+
+stateApp.get('/', (req, res, next) => {
+    const stateSql = 'SELECT * FROM estados';
+    dbQuery(stateSql).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+stateApp.get('/:id', (req, res, next) => {
+    const stateSql = 'SELECT * FROM estados WHERE pk_estado_id = ?';
+    const stateId = parseInt(req.params.id, 10);
+    dbQuery(stateSql, [stateId]).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+stateApp.get('/region/:id', (req, res, next) => {
+    const stateSql = 'SELECT * FROM estados WHERE fk_regiao_id = ?';
+    const regionId = parseInt(req.params.id, 10);
+    dbQuery(stateSql, [regionId]).then((result) => {
+        req.result = result;
+        return response(req, res);
+    }, (error) => {
+        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
+        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+    });
+});
+
+module.exports = stateApp;
diff --git a/src/libs/routes/states.js b/src/libs/routes/states.js
deleted file mode 100644
index 23cc7a3d..00000000
--- a/src/libs/routes/states.js
+++ /dev/null
@@ -1,77 +0,0 @@
-const express = require('express');
-
-const xml = require('js2xmlparser');
-
-const stateApp = express();
-
-const libs = `${process.cwd()}/libs`;
-
-const log = require(`${libs}/log`)(module);
-
-const conn = require(`${libs}/db/monet`);
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ state: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
-
-stateApp.get('/', (req, res, next) => {
-    const stateSql = 'SELECT * FROM estados';
-    conn.query(stateSql, true).then(
-        (dbResult) => {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        },
-        (dbError) => {
-            log.error(`SQL query execution error: ${dbError.message}`);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        }
-    );
-});
-
-stateApp.get('/:id', (req, res, next) => {
-    const stateSql = 'SELECT * FROM estados WHERE pk_estado_id = ?';
-    const stateId = parseInt(req.params.id, 10);
-    conn.prepare(stateSql, true).then((dbQuery) => {
-        dbQuery.exec([stateId]).then(
-            (dbResult) => {
-                log.debug(dbResult);
-                req.result = dbResult;
-                response(req, res);
-            },
-            (dbError) => {
-                log.error(`SQL query execution error: ${dbError.message}`);
-                // FIXME: change response to HTTP 501 status
-                res.json({ error: 'An internal error has occurred' }).end();
-            }
-        );
-    });
-});
-
-stateApp.get('/region/:id', (req, res, next) => {
-    const stateSql = 'SELECT * FROM estados WHERE fk_regiao_id = ?';
-    const regionId = parseInt(req.params.id, 10);
-    conn.prepare(stateSql, true).then((dbQuery) => {
-        dbQuery.exec([regionId]).then(
-            (dbResult) => {
-                log.debug(dbResult);
-                req.result = dbResult;
-                response(req, res);
-            },
-            (dbError) => {
-                log.error(`SQL query execution error: ${dbError.message}`);
-                // FIXME: change response to HTTP 501 status
-                res.json({ error: 'An internal error has occurred' }).end();
-            }
-        );
-    });
-});
-
-module.exports = stateApp;
-- 
GitLab


From 9f1da16d3c55cfb5c600c494280b27e64abd7ba4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Mon, 29 Aug 2016 14:00:06 -0300
Subject: [PATCH 14/58] Clear build directory

Remove all files from build directory. The rationale is that this
directory is updated frequently during the development process and might
add too much 'noise' in the commits in the repository.
---
 build/config.json                |  16 --
 build/libs/app.js                |  43 -----
 build/libs/config.js             |   7 -
 build/libs/db/monet.js           |  24 ---
 build/libs/db/query_decorator.js |  22 ---
 build/libs/enrollment/all.js     |   1 -
 build/libs/enrollment/city.js    |   1 -
 build/libs/enrollment/common.js  |  15 --
 build/libs/enrollment/country.js |   1 -
 build/libs/enrollment/region.js  |   1 -
 build/libs/enrollment/state.js   |   1 -
 build/libs/log.js                |  33 ----
 build/libs/query_decorator.js    |  22 ---
 build/libs/routes/api.js         | 308 -------------------------------
 build/libs/routes/cities.js      |  85 ---------
 build/libs/routes/regions.js     |  54 ------
 build/libs/routes/states.js      |  70 -------
 build/logs/.gitignore            |   1 -
 build/server.js                  |  18 --
 build/test/test.js               | 139 --------------
 20 files changed, 862 deletions(-)
 delete mode 100644 build/config.json
 delete mode 100644 build/libs/app.js
 delete mode 100644 build/libs/config.js
 delete mode 100644 build/libs/db/monet.js
 delete mode 100644 build/libs/db/query_decorator.js
 delete mode 100644 build/libs/enrollment/all.js
 delete mode 100644 build/libs/enrollment/city.js
 delete mode 100644 build/libs/enrollment/common.js
 delete mode 100644 build/libs/enrollment/country.js
 delete mode 100644 build/libs/enrollment/region.js
 delete mode 100644 build/libs/enrollment/state.js
 delete mode 100644 build/libs/log.js
 delete mode 100644 build/libs/query_decorator.js
 delete mode 100644 build/libs/routes/api.js
 delete mode 100644 build/libs/routes/cities.js
 delete mode 100644 build/libs/routes/regions.js
 delete mode 100644 build/libs/routes/states.js
 delete mode 100644 build/logs/.gitignore
 delete mode 100644 build/server.js
 delete mode 100644 build/test/test.js

diff --git a/build/config.json b/build/config.json
deleted file mode 100644
index 076d753b..00000000
--- a/build/config.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-    "port": 3000,
-    "monetdb": {
-        "host": "simcaqdb1",
-        "port": 50000,
-        "dbname": "simcaq_dev",
-        "user": "monetdb",
-        "password":"monetdb",
-        "nrConnections": 16
-    },
-    "default": {
-        "api": {
-            "version" : "v1"
-        }
-    }
-}
diff --git a/build/libs/app.js b/build/libs/app.js
deleted file mode 100644
index a678b1c4..00000000
--- a/build/libs/app.js
+++ /dev/null
@@ -1,43 +0,0 @@
-'use strict';
-
-var express = require('express');
-var cookieParser = require('cookie-parser');
-var bodyParser = require('body-parser');
-var methodOverride = require('method-override');
-var cors = require('cors');
-
-var log = require('./log')(module);
-
-var api = require('./routes/api');
-var states = require('./routes/states');
-var regions = require('./routes/regions');
-var cities = require('./routes/cities');
-
-var app = express();
-
-app.use(bodyParser.json());
-app.use(bodyParser.urlencoded({ extended: false }));
-app.use(cookieParser());
-app.use(cors());
-app.use(methodOverride());
-
-app.use('/v1/', api);
-app.use('/v1/states', states);
-app.use('/v1/regions', regions);
-app.use('/v1/cities', cities);
-
-// catch 404 and forward to error handler
-app.use(function (req, res, next) {
-    res.status(404);
-    log.debug('%s %d %s', req.method, res.statusCode, req.url);
-    res.json({ error: 'Not found' }).end();
-});
-
-// error handlers
-app.use(function (err, req, res, next) {
-    res.status(err.status || 500);
-    log.error('%s %d %s', req.method, res.statusCode, err.message);
-    res.json({ error: err.message }).end();
-});
-
-module.exports = app;
\ No newline at end of file
diff --git a/build/libs/config.js b/build/libs/config.js
deleted file mode 100644
index 2f7bd18e..00000000
--- a/build/libs/config.js
+++ /dev/null
@@ -1,7 +0,0 @@
-'use strict';
-
-var nconf = require('nconf');
-
-nconf.argv().env().file({ file: process.cwd() + '/config.json' });
-
-module.exports = nconf;
\ No newline at end of file
diff --git a/build/libs/db/monet.js b/build/libs/db/monet.js
deleted file mode 100644
index c8d29099..00000000
--- a/build/libs/db/monet.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict';
-
-var MonetDBPool = require('monetdb-pool');
-
-var libs = process.cwd() + '/libs';
-
-var config = require(libs + '/config');
-
-var poolOptions = {
-    nrConnections: config.get('monetdb:nrConnections')
-};
-
-var options = {
-    host: config.get('monetdb:host'),
-    port: config.get('monetdb:port'),
-    dbname: config.get('monetdb:dbname'),
-    user: config.get('monetdb:user'),
-    password: config.get('monetdb:password')
-};
-
-var conn = new MonetDBPool(poolOptions, options);
-conn.connect();
-
-module.exports = conn;
\ No newline at end of file
diff --git a/build/libs/db/query_decorator.js b/build/libs/db/query_decorator.js
deleted file mode 100644
index ce0697d7..00000000
--- a/build/libs/db/query_decorator.js
+++ /dev/null
@@ -1,22 +0,0 @@
-"use strict";
-
-var libs = process.cwd() + "/libs";
-var log = require(libs + "/log")(module);
-var conn = require(libs + "/db/monet");
-
-/**
- * Basic decorator to wrap SQL query strings
- */
-exports.execQuery = function (sqlQuery, sqlQueryParams) {
-    log.debug("Executing SQL query '" + sqlQuery + "' with params '" + sqlQueryParams + "'");
-    conn.prepare(sqlQuery, true).then(function (dbQuery) {
-        dbQuery.exec(sqlQueryParams).then(function (dbResult) {
-            log.debug(dbResult.data);
-            log.debug("Query result: " + dbResult.data);
-            return dbResult;
-        }, function (error) {
-            log.error("SQL query execution error: " + error.message);
-            return error;
-        });
-    });
-};
\ No newline at end of file
diff --git a/build/libs/enrollment/all.js b/build/libs/enrollment/all.js
deleted file mode 100644
index 9a390c31..00000000
--- a/build/libs/enrollment/all.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";
\ No newline at end of file
diff --git a/build/libs/enrollment/city.js b/build/libs/enrollment/city.js
deleted file mode 100644
index 9a390c31..00000000
--- a/build/libs/enrollment/city.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";
\ No newline at end of file
diff --git a/build/libs/enrollment/common.js b/build/libs/enrollment/common.js
deleted file mode 100644
index 9c788951..00000000
--- a/build/libs/enrollment/common.js
+++ /dev/null
@@ -1,15 +0,0 @@
-'use strict';
-
-var libs = process.cwd() + '/libs';
-
-var log = require(libs + '/log')(module);
-
-var sqlDecorator = require(libs + '/query_decorator');
-
-var yearRange = function yearRange() {
-    var yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)' + 'AS end_year FROM turmas AS t';
-    log.debug('Generated SQL query for enrollments\' year range');
-    return sqlDecorator.execQuery(yearSql, []);
-};
-
-module.exports = yearRange;
\ No newline at end of file
diff --git a/build/libs/enrollment/country.js b/build/libs/enrollment/country.js
deleted file mode 100644
index 9a390c31..00000000
--- a/build/libs/enrollment/country.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";
\ No newline at end of file
diff --git a/build/libs/enrollment/region.js b/build/libs/enrollment/region.js
deleted file mode 100644
index 9a390c31..00000000
--- a/build/libs/enrollment/region.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";
\ No newline at end of file
diff --git a/build/libs/enrollment/state.js b/build/libs/enrollment/state.js
deleted file mode 100644
index 9a390c31..00000000
--- a/build/libs/enrollment/state.js
+++ /dev/null
@@ -1 +0,0 @@
-"use strict";
\ No newline at end of file
diff --git a/build/libs/log.js b/build/libs/log.js
deleted file mode 100644
index e93d2ff2..00000000
--- a/build/libs/log.js
+++ /dev/null
@@ -1,33 +0,0 @@
-'use strict';
-
-var winston = require('winston');
-
-winston.emitErrs = true;
-
-function getFilePath(module) {
-    // using filename in log statements
-    return module.filename.split('/').slice(-2).join('/');
-}
-
-function logger(module) {
-    return new winston.Logger({
-        transports: [new winston.transports.File({
-            level: 'info',
-            filename: process.cwd() + '/logs/all.log',
-            handleException: true,
-            json: false,
-            maxSize: 5242880, // 5MB
-            maxFiles: 2,
-            colorize: false
-        }), new winston.transports.Console({
-            level: 'debug',
-            label: getFilePath(module),
-            handleException: true,
-            json: true,
-            colorize: true
-        })],
-        exitOnError: false
-    });
-}
-
-module.exports = logger;
\ No newline at end of file
diff --git a/build/libs/query_decorator.js b/build/libs/query_decorator.js
deleted file mode 100644
index ce0697d7..00000000
--- a/build/libs/query_decorator.js
+++ /dev/null
@@ -1,22 +0,0 @@
-"use strict";
-
-var libs = process.cwd() + "/libs";
-var log = require(libs + "/log")(module);
-var conn = require(libs + "/db/monet");
-
-/**
- * Basic decorator to wrap SQL query strings
- */
-exports.execQuery = function (sqlQuery, sqlQueryParams) {
-    log.debug("Executing SQL query '" + sqlQuery + "' with params '" + sqlQueryParams + "'");
-    conn.prepare(sqlQuery, true).then(function (dbQuery) {
-        dbQuery.exec(sqlQueryParams).then(function (dbResult) {
-            log.debug(dbResult.data);
-            log.debug("Query result: " + dbResult.data);
-            return dbResult;
-        }, function (error) {
-            log.error("SQL query execution error: " + error.message);
-            return error;
-        });
-    });
-};
\ No newline at end of file
diff --git a/build/libs/routes/api.js b/build/libs/routes/api.js
deleted file mode 100644
index 812eae43..00000000
--- a/build/libs/routes/api.js
+++ /dev/null
@@ -1,308 +0,0 @@
-'use strict';
-
-var express = require('express');
-
-var xml = require('js2xmlparser');
-
-var enrollmentApp = express();
-
-var libs = process.cwd() + '/libs';
-
-var log = require(libs + '/log')(module);
-
-var conn = require(libs + '/db/monet');
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ state: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
-
-enrollmentApp.get('/', function (req, res) {
-    res.json({ msg: 'SimCAQ API is running' });
-});
-
-/**
- * Complete range of the enrollments dataset
- *
- * Returns a tuple of start and ending years of the complete enrollments dataset.
- */
-enrollmentApp.get('/year_range', function (req, res) {
-    var yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)' + 'AS end_year FROM turmas AS t';
-
-    conn.query(yearSql, true).then(function (dbResult) {
-        log.debug(dbResult);
-        req.result = dbResult;
-        response(req, res);
-    }, function (dbError) {
-        log.error('SQL query execution error: ' + dbError.message);
-        // FIXME: change response to HTTP 501 status
-        res.json({ error: 'An internal error has occurred' }).end();
-    });
-});
-
-enrollmentApp.get('/education_level', function (req, res) {
-    var edLevelSql = 'SELECT pk_etapa_ensino_id AS id, desc_etapa AS ' + 'education_level FROM etapa_ensino';
-    conn.query(edLevelSql, true).then(function (dbResult) {
-        log.debug(dbResult);
-        req.result = dbResult;
-        response(req, res);
-    }, function (dbError) {
-        log.error('SQL query error: ' + dbError.message);
-        // FIXME: change response to HTTP 501 status
-        res.json({ error: 'An internal error has occurred' }).end();
-    });
-});
-
-enrollmentApp.get('/data', function (req, res) {
-    log.debug(req.query);
-    log.debug(req.query.met);
-    log.debug(req.query.dim);
-    var schoolClassSql = 'SELECT * FROM turmas';
-    conn.query(schoolClassSql, true).then(function (dbResult) {
-        log.debug(dbResult);
-        req.result = dbResult;
-        response(req, res);
-    }, function (dbError) {
-        log.error('SQL query error: ' + dbError.message);
-        // FIXME: change response to HTTP 501 status
-        res.json({ error: 'An internal error has occurred' }).end();
-    });
-});
-
-enrollmentApp.use('/enrollments', function (req, res, next) {
-    var params = req.query;
-    req.paramCnt = 0;
-
-    if (typeof params.id !== 'undefined') {
-        req.id = parseInt(params.id, 10);
-        req.paramCnt += 1;
-    }
-
-    if (typeof params.location_id !== 'undefined') {
-        req.location_id = parseInt(params.location_id, 10);
-        req.paramCnt += 1;
-    }
-
-    if (typeof params.adm_dependency_id !== 'undefined') {
-        req.adm_dependency_id = parseInt(params.adm_dependency_id, 10);
-        req.paramCnt += 1;
-    }
-
-    if (typeof params.census_year !== 'undefined') {
-        req.census_year = parseInt(params.census_year, 10);
-        req.paramCnt += 1;
-    }
-
-    if (typeof params.education_level_id !== 'undefined') {
-        req.education_level_id = parseInt(params.education_level_id, 10);
-        req.paramCnt += 1;
-    }
-
-    next();
-});
-
-enrollmentApp.use('/enrollments', function (req, res, next) {
-    var params = req.query;
-    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
-        log.debug('Using enrollments query for regions');
-        req.sqlQuery = 'SELECT r.nome AS name, COALESCE(SUM(t.num_matriculas), 0) AS total ' + 'FROM regioes AS r ' + 'INNER JOIN estados AS e ON r.pk_regiao_id = e.fk_regiao_id ' + 'INNER JOIN municipios AS m ON e.pk_estado_id = m.fk_estado_id ' + 'LEFT OUTER JOIN turmas AS t ON ( ' + 'm.pk_municipio_id = t.fk_municipio_id ';
-        req.sqlQueryParams = [];
-
-        if (typeof req.census_year !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.ano_censo = ?';
-            req.sqlQueryParams.push(req.census_year);
-        }
-
-        if (typeof req.adm_dependency_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
-            req.sqlQueryParams.push(req.adm_dependency_id);
-        }
-
-        if (typeof req.location_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.id_localizacao = ?';
-            req.sqlQueryParams.push(req.location_id);
-        }
-
-        if (typeof req.education_level_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
-            req.sqlQueryParams.push(req.education_level_id);
-        }
-
-        req.sqlQuery += ')';
-        if (typeof req.id !== 'undefined') {
-            req.sqlQuery += ' WHERE ';
-            req.sqlQuery += 'r.pk_regiao_id = ?';
-            req.sqlQueryParams.push(req.id);
-        }
-        req.sqlQuery += ' GROUP BY r.nome';
-    }
-    next();
-});
-
-enrollmentApp.use('/enrollments', function (req, res, next) {
-    var params = req.query;
-    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
-        log.debug('Using enrollments query for states');
-        req.sqlQuery = 'SELECT e.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total ' + 'FROM estados AS e ' + 'INNER JOIN municipios AS m ON m.fk_estado_id = e.pk_estado_id ' + 'LEFT OUTER JOIN turmas AS t ON (' + 'm.pk_municipio_id = t.fk_municipio_id ';
-        req.sqlQueryParams = [];
-
-        if (typeof req.census_year !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.ano_censo = ?';
-            req.sqlQueryParams.push(req.census_year);
-        }
-
-        if (typeof req.adm_dependency_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
-            req.sqlQueryParams.push(req.adm_dependency_id);
-        }
-
-        if (typeof req.location_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.id_localizacao = ?';
-            req.sqlQueryParams.push(req.location_id);
-        }
-
-        if (typeof req.education_level_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
-            req.sqlQueryParams.push(req.education_level_id);
-        }
-
-        req.sqlQuery += ')';
-
-        if (typeof req.id !== 'undefined') {
-            req.sqlQuery += ' WHERE ';
-            req.sqlQuery += 'e.pk_estado_id = ?';
-            req.sqlQueryParams.push(req.id);
-        }
-
-        req.sqlQuery += ' GROUP BY e.nome';
-    }
-    next();
-});
-
-enrollmentApp.use('/enrollments', function (req, res, next) {
-    var params = req.query;
-    if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
-        log.debug('Using enrollments query for cities');
-        req.sqlQuery = 'SELECT m.nome AS name, COALESCE(SUM(t.num_matriculas), 0) as total ' + 'FROM municipios AS m ' + 'LEFT OUTER JOIN turmas AS t ON ( ' + 'm.pk_municipio_id = t.fk_municipio_id';
-        req.sqlQueryParams = [];
-
-        if (typeof req.census_year !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.ano_censo = ?';
-            req.sqlQueryParams.push(req.census_year);
-        }
-
-        if (typeof req.adm_dependency_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
-            req.sqlQueryParams.push(req.adm_dependency_id);
-        }
-
-        if (typeof req.location_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.id_localizacao = ?';
-            req.sqlQueryParams.push(req.location_id);
-        }
-
-        if (typeof req.education_level_id !== 'undefined') {
-            req.sqlQuery += ' AND ';
-            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
-            req.sqlQueryParams.push(req.education_level_id);
-        }
-
-        req.sqlQuery += ')';
-
-        if (typeof req.id !== 'undefined') {
-            req.sqlQuery += ' WHERE ';
-            req.sqlQuery += 'm.pk_municipio_id = ?';
-            req.sqlQueryParams.push(req.id);
-        }
-
-        req.sqlQuery += 'GROUP BY m.nome';
-    }
-    next();
-});
-
-enrollmentApp.use('/enrollments', function (req, res, next) {
-    var params = req.query;
-    if (typeof params.aggregate === 'undefined') {
-        log.debug('Using enrollments query for the whole country');
-        req.sqlQuery = 'SELECT \'Brasil\' AS name, COALESCE(SUM(t.num_matriculas),0) AS total ' + 'FROM turmas AS t';
-        req.sqlQueryParams = [];
-
-        if (req.paramCnt > 0) {
-            req.sqlQuery += ' WHERE ';
-        }
-
-        if (typeof req.census_year !== 'undefined') {
-            req.sqlQuery += 't.ano_censo = ?';
-            req.sqlQueryParams.push(req.census_year);
-        }
-
-        if (typeof req.adm_dependency_id !== 'undefined') {
-            if (req.sqlQueryParams.length > 0) {
-                req.sqlQuery += ' AND ';
-            }
-            req.sqlQuery += 't.fk_dependencia_adm_id = ?';
-            req.sqlQueryParams.push(req.adm_dependency_id);
-        }
-
-        if (typeof req.location_id !== 'undefined') {
-            if (req.sqlQueryParams.length > 0) {
-                req.sqlQuery += ' AND ';
-            }
-            req.sqlQuery += 't.id_localizacao = ?';
-            req.sqlQueryParams.push(req.location_id);
-        }
-
-        if (typeof req.education_level_id !== 'undefined') {
-            if (req.sqlQueryParams.length > 0) {
-                req.sqlQuery += ' AND ';
-            }
-            req.sqlQuery += 't.fk_etapa_ensino_id = ?';
-            req.sqlQueryParams.push(req.education_level_id);
-        }
-    }
-    next();
-});
-
-enrollmentApp.get('/enrollments', function (req, res, next) {
-    log.debug('Request parameters: ' + req);
-    if (typeof req.sqlQuery === 'undefined') {
-        // Should only happen if there is a bug in the chaining of the
-        // '/enrollments' route, since when no +aggregate+ parameter is given,
-        // it defaults to use the query for the whole country.
-        log.error('BUG -- No SQL query was found to be executed!');
-        res.send('Request could not be satisfied due to an internal error');
-    } else {
-        log.debug('SQL query: ${ req.sqlQuery }?');
-        log.debug(req.sqlQuery);
-
-        conn.prepare(req.sqlQuery, true).then(function (dbQuery) {
-            dbQuery.exec(req.sqlQueryParams).then(function (dbResult) {
-                log.debug(dbResult);
-                req.result = dbResult;
-                response(req, res);
-            }, function (dbError) {
-                log.error('SQL query execution error: ' + dbError.message);
-                // FIXME: change response to HTTP 501 status
-                res.json({ error: 'An internal error has occurred' }).end();
-            });
-        });
-    }
-});
-
-module.exports = enrollmentApp;
\ No newline at end of file
diff --git a/build/libs/routes/cities.js b/build/libs/routes/cities.js
deleted file mode 100644
index 6a0048f2..00000000
--- a/build/libs/routes/cities.js
+++ /dev/null
@@ -1,85 +0,0 @@
-'use strict';
-
-var express = require('express');
-
-var xml = require('js2xmlparser');
-
-var cityApp = express();
-
-var libs = process.cwd() + '/libs';
-
-var log = require(libs + '/log')(module);
-
-var conn = require(libs + '/db/monet');
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ city: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
-
-cityApp.get('/', function (req, res) {
-    conn.query('SELECT * FROM municipios', true).then(function (dbResult) {
-        log.debug(dbResult);
-        req.result = dbResult;
-        response(req, res);
-    }, function (dbError) {
-        log.error('SQL query execution error: ' + dbError.message);
-        // FIXME: change response to HTTP 501 status
-        res.json({ error: 'An internal error has occurred' }).end();
-    });
-});
-
-cityApp.get('/:id', function (req, res) {
-    var citySql = 'SELECT * FROM municipios WHERE pk_municipio_id = ?';
-    var cityId = parseInt(req.params.id, 10);
-    conn.prepare(citySql, true).then(function (dbQuery) {
-        dbQuery.exec([cityId]).then(function (dbResult) {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        }, function (dbError) {
-            log.error('SQL query execution error: ' + dbError.message);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        });
-    });
-});
-
-cityApp.get('/ibge/:id', function (req, res) {
-    var citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
-    var cityIbgeCode = req.params.id;
-    conn.prepare(citySql, true).then(function (dbQuery) {
-        dbQuery.exec([cityIbgeCode]).then(function (dbResult) {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        }, function (dbError) {
-            log.error('SQL query execution error: ' + dbError.message);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        });
-    });
-});
-
-cityApp.get('/state/:id', function (req, res) {
-    var citySql = 'SELECT * FROM municipios WHERE fk_estado_id = ?';
-    var stateId = parseInt(req.params.id, 10);
-    conn.prepare(citySql, true).then(function (dbQuery) {
-        dbQuery.exec([stateId]).then(function (dbResult) {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        }, function (dbError) {
-            log.error('SQL query execution error: ' + dbError.message);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        });
-    });
-});
-
-module.exports = cityApp;
\ No newline at end of file
diff --git a/build/libs/routes/regions.js b/build/libs/routes/regions.js
deleted file mode 100644
index 75cf2bee..00000000
--- a/build/libs/routes/regions.js
+++ /dev/null
@@ -1,54 +0,0 @@
-'use strict';
-
-var express = require('express');
-
-var xml = require('js2xmlparser');
-
-var regionApp = express();
-
-var libs = process.cwd() + '/libs';
-
-var log = require(libs + '/log')(module);
-
-var conn = require(libs + '/db/monet');
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ state: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
-
-regionApp.get('/', function (req, res) {
-    var regionSql = 'SELECT * FROM regioes';
-    conn.query(regionSql, true).then(function (dbResult) {
-        log.debug(dbResult);
-        req.result = dbResult;
-        response(req, res);
-    }, function (dbError) {
-        log.error('SQL query execution error: ' + dbError.message);
-        // FIXME: change response to HTTP 501 status
-        res.json({ error: 'An internal error has occurred' }).end();
-    });
-});
-
-regionApp.get('/:id', function (req, res) {
-    var regionSql = 'SELECT * FROM regioes WHERE pk_regiao_id = ?';
-    var regionId = parseInt(req.params.id, 10);
-    conn.prepare(regionSql, true).then(function (dbQuery) {
-        dbQuery.exec([regionId]).then(function (dbResult) {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        }, function (dbError) {
-            log.error('SQL query execution error: ' + dbError.message);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        });
-    });
-});
-
-module.exports = regionApp;
\ No newline at end of file
diff --git a/build/libs/routes/states.js b/build/libs/routes/states.js
deleted file mode 100644
index 8579f392..00000000
--- a/build/libs/routes/states.js
+++ /dev/null
@@ -1,70 +0,0 @@
-'use strict';
-
-var express = require('express');
-
-var xml = require('js2xmlparser');
-
-var stateApp = express();
-
-var libs = process.cwd() + '/libs';
-
-var log = require(libs + '/log')(module);
-
-var conn = require(libs + '/db/monet');
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ state: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
-
-stateApp.get('/', function (req, res, next) {
-    var stateSql = 'SELECT * FROM estados';
-    conn.query(stateSql, true).then(function (dbResult) {
-        log.debug(dbResult);
-        req.result = dbResult;
-        response(req, res);
-    }, function (dbError) {
-        log.error('SQL query execution error: ' + dbError.message);
-        // FIXME: change response to HTTP 501 status
-        res.json({ error: 'An internal error has occurred' }).end();
-    });
-});
-
-stateApp.get('/:id', function (req, res, next) {
-    var stateSql = 'SELECT * FROM estados WHERE pk_estado_id = ?';
-    var stateId = parseInt(req.params.id, 10);
-    conn.prepare(stateSql, true).then(function (dbQuery) {
-        dbQuery.exec([stateId]).then(function (dbResult) {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        }, function (dbError) {
-            log.error('SQL query execution error: ' + dbError.message);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        });
-    });
-});
-
-stateApp.get('/region/:id', function (req, res, next) {
-    var stateSql = 'SELECT * FROM estados WHERE fk_regiao_id = ?';
-    var regionId = parseInt(req.params.id, 10);
-    conn.prepare(stateSql, true).then(function (dbQuery) {
-        dbQuery.exec([regionId]).then(function (dbResult) {
-            log.debug(dbResult);
-            req.result = dbResult;
-            response(req, res);
-        }, function (dbError) {
-            log.error('SQL query execution error: ' + dbError.message);
-            // FIXME: change response to HTTP 501 status
-            res.json({ error: 'An internal error has occurred' }).end();
-        });
-    });
-});
-
-module.exports = stateApp;
\ No newline at end of file
diff --git a/build/logs/.gitignore b/build/logs/.gitignore
deleted file mode 100644
index 397b4a76..00000000
--- a/build/logs/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.log
diff --git a/build/server.js b/build/server.js
deleted file mode 100644
index 5f2171cc..00000000
--- a/build/server.js
+++ /dev/null
@@ -1,18 +0,0 @@
-'use strict';
-
-var debug = require('debug')('node-express-base');
-
-var libs = process.cwd() + '/libs';
-
-var config = require(libs + '/config');
-
-var log = require(libs + '/log')(module);
-
-var app = require(libs + '/app');
-
-app.set('port', config.get('port') || 3000);
-
-var server = app.listen(app.get('port'), function () {
-    debug('Express server listening on port ' + server.address().port);
-    log.info('Express server listening on port ' + config.get('port'));
-});
\ No newline at end of file
diff --git a/build/test/test.js b/build/test/test.js
deleted file mode 100644
index 55caa55c..00000000
--- a/build/test/test.js
+++ /dev/null
@@ -1,139 +0,0 @@
-'use strict';
-
-var chai = require('chai');
-var chaiHttp = require('chai-http');
-var assert = chai.assert;
-var expect = chai.expect;
-var should = chai.should(); // actually call the function
-var server = require('../libs/app');
-
-chai.use(chaiHttp);
-
-describe('request enrollments', function () {
-    it('should list enrollments', function (done) {
-        chai.request(server).get('/v1/enrollments').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result[0].should.have.property('name');
-            res.body.result[0].should.have.property('total');
-            done();
-        });
-    });
-});
-
-describe('request regions', function () {
-    it('should list all regions', function (done) {
-        chai.request(server).get('/v1/regions').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result[0].should.have.property('pk_regiao_id');
-            res.body.result[0].should.have.property('nome');
-            done();
-        });
-    });
-
-    it('should list region by id', function (done) {
-        chai.request(server).get('/v1/regions/1').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result.should.have.length(1);
-            res.body.result[0].should.have.property('pk_regiao_id');
-            res.body.result[0].should.have.property('nome');
-            done();
-        });
-    });
-});
-
-describe('request states', function () {
-
-    it('should list all states', function (done) {
-        chai.request(server).get('/v1/states').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result[0].should.have.property('pk_estado_id');
-            res.body.result[0].should.have.property('fk_regiao_id');
-            res.body.result[0].should.have.property('nome');
-            done();
-        });
-    });
-
-    it('should list a state by id', function (done) {
-        chai.request(server).get('/v1/states/11').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result.should.have.length(1);
-            res.body.result[0].should.have.property('pk_estado_id');
-            res.body.result[0].should.have.property('fk_regiao_id');
-            res.body.result[0].should.have.property('nome');
-            done();
-        });
-    });
-
-    it('should list states by region id', function (done) {
-        chai.request(server).get('/v1/states/region/1').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result[0].should.have.property('pk_estado_id');
-            res.body.result[0].should.have.property('fk_regiao_id');
-            res.body.result[0].should.have.property('nome');
-            done();
-        });
-    });
-});
-
-describe('request cities', function () {
-
-    it('should list all cities', function (done) {
-        chai.request(server).get('/v1/cities').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result[0].should.have.property('pk_municipio_id');
-            res.body.result[0].should.have.property('fk_estado_id');
-            res.body.result[0].should.have.property('nome');
-            res.body.result[0].should.have.property('codigo_ibge');
-            done();
-        });
-    });
-
-    it('should list a city by id', function (done) {
-        chai.request(server).get('/v1/cities/1').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result[0].should.have.property('pk_municipio_id');
-            res.body.result[0].should.have.property('fk_estado_id');
-            res.body.result[0].should.have.property('nome');
-            res.body.result[0].should.have.property('codigo_ibge');
-            done();
-        });
-    });
-
-    it('should list a city by codigo_ibge', function (done) {
-        chai.request(server).get('/v1/cities/ibge/1200013').end(function (err, res) {
-            res.should.have.status(200);
-            res.should.be.json;
-            res.body.should.have.property('result');
-            res.body.result.should.be.a('array');
-            res.body.result[0].should.have.property('pk_municipio_id');
-            res.body.result[0].should.have.property('fk_estado_id');
-            res.body.result[0].should.have.property('nome');
-            res.body.result[0].should.have.property('codigo_ibge');
-            done();
-        });
-    });
-});
\ No newline at end of file
-- 
GitLab


From 373934ddde12e3c3bed300893cd03643494d6515 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@c3sl.ufpr.br>
Date: Mon, 29 Aug 2016 14:01:24 -0300
Subject: [PATCH 15/58] Update .gitignore to include build directory

---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index f4e4627c..6cb01f09 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@ results
 
 npm-debug.log
 node_modules/
+build/*
-- 
GitLab


From f24c3c1174c8c7a453f377747e256b7f7f55f5e8 Mon Sep 17 00:00:00 2001
From: Lucas Gabriel Lima <lgl15@inf.ufpr.br>
Date: Wed, 31 Aug 2016 09:44:17 -0300
Subject: [PATCH 16/58] add gulp and gulpfile to project

---
 gulpfile.js  | 5 +++++
 package.json | 1 +
 2 files changed, 6 insertions(+)
 create mode 100644 gulpfile.js

diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 00000000..99dcbc68
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,5 @@
+var gulp = require('gulp');
+
+gulp.task('default', function() {
+
+});
diff --git a/package.json b/package.json
index 70eae984..04f97575 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
   "devDependencies": {
     "chai": "^3.5.0",
     "chai-http": "^3.0.0",
+    "gulp": "^3.9.1",
     "mocha": "^2.5.3"
   }
 }
-- 
GitLab


From 55710fa035718de4a3182c87a7f2b30f3e1cc1f2 Mon Sep 17 00:00:00 2001
From: Lucas Gabriel Lima <lgl15@inf.ufpr.br>
Date: Thu, 1 Sep 2016 09:50:01 -0300
Subject: [PATCH 17/58] add gulp-mocha library and task for testing

---
 gulpfile.js  | 12 ++++++++++++
 package.json |  1 +
 2 files changed, 13 insertions(+)

diff --git a/gulpfile.js b/gulpfile.js
index 99dcbc68..06a87e24 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,5 +1,17 @@
 var gulp = require('gulp');
+var mocha = require('gulp-mocha');
 
 gulp.task('default', function() {
 
 });
+
+gulp.task('test', function(){
+  gulp.src('test/test.js', {read: false})
+    .pipe(mocha())
+    .once('error', function(){
+      process.exit(1);
+    })
+    .once('end', function(){
+      process.exit();
+    })
+});
diff --git a/package.json b/package.json
index 04f97575..3e11046c 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
     "chai": "^3.5.0",
     "chai-http": "^3.0.0",
     "gulp": "^3.9.1",
+    "gulp-mocha": "^3.0.1",
     "mocha": "^2.5.3"
   }
 }
-- 
GitLab


From 51a190eceb3c8c0c5851512631aed0b7469f0e84 Mon Sep 17 00:00:00 2001
From: Lucas Gabriel Lima <lgl15@inf.ufpr.br>
Date: Fri, 2 Sep 2016 11:02:06 -0300
Subject: [PATCH 18/58] add browser-sync and gulp-nodemon

---
 gulpfile.js  | 5 ++++-
 package.json | 5 +++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/gulpfile.js b/gulpfile.js
index 06a87e24..4274caa8 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,7 +1,10 @@
 var gulp = require('gulp');
 var mocha = require('gulp-mocha');
+var browserSync = require('browser-sync');
+var nodemon = require('gulp-nodemon');
 
-gulp.task('default', function() {
+
+gulp.task('default', ['browser-sync'], function() {
 
 });
 
diff --git a/package.json b/package.json
index 3e11046c..c7f218a2 100644
--- a/package.json
+++ b/package.json
@@ -26,10 +26,15 @@
   },
   "license": "MIT",
   "devDependencies": {
+    "browser-sync": "^2.14.3",
     "chai": "^3.5.0",
     "chai-http": "^3.0.0",
     "gulp": "^3.9.1",
     "gulp-mocha": "^3.0.1",
+    "gulp-nodemon": "^2.1.0",
+    "gulp-plumber": "^1.1.0",
+    "gulp-rename": "^1.2.2",
+    "gulp-uglify": "^2.0.0",
     "mocha": "^2.5.3"
   }
 }
-- 
GitLab


From ed16d9fd048ec3a8840f1b0a7e0f7d112667bfb4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?=
 <joao@portalmec5.c3local>
Date: Fri, 2 Sep 2016 11:29:34 -0300
Subject: [PATCH 19/58] Add babel-cli module to packages
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <joao@portalmec5.c3local>
---
 package.json | 1 +
 1 file changed, 1 insertion(+)

diff --git a/package.json b/package.json
index 141451dd..756a3378 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
   },
   "license": "MIT",
   "devDependencies": {
+    "babel-cli": "^6.11.4",
     "babel-preset-es2015": "^6.13.2",
     "babelify": "^7.3.0",
     "browserify": "^13.1.0",
-- 
GitLab


From 966ed546e5b5ca164123e5861b16350c32ff21dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 11:35:51 -0300
Subject: [PATCH 20/58] Add babel-register module to packages
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 package.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/package.json b/package.json
index 56385c05..8739650a 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,9 @@
   "license": "MIT",
   "devDependencies": {
     "babel-cli": "^6.11.4",
+    "babel-core": "^6.14.0",
     "babel-preset-es2015": "^6.13.2",
+    "babel-register": "^6.14.0",
     "babelify": "^7.3.0",
     "browserify": "^13.1.0",
     "chai": "^3.5.0",
-- 
GitLab


From d7885f74d4d97c3f998d8d70096c81655e0c732a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 12:07:22 -0300
Subject: [PATCH 21/58] Merge routes from cities.js into city.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/cities.js | 42 --------------------
 src/libs/routes/city.js   | 81 +++++++++++----------------------------
 2 files changed, 23 insertions(+), 100 deletions(-)
 delete mode 100644 src/libs/routes/cities.js

diff --git a/src/libs/routes/cities.js b/src/libs/routes/cities.js
deleted file mode 100644
index fd030263..00000000
--- a/src/libs/routes/cities.js
+++ /dev/null
@@ -1,42 +0,0 @@
-var express = require('express')
-var xml = require('js2xmlparser')
-var router = express.Router()
-var squel = require('squel')
-
-var libs = process.cwd() + '/libs/'
-
-var log = require(libs + 'log')(module)
-var config = require(libs + 'config')
-
-var conn = require(libs + 'db/monet')
-var query = require(libs + 'middlewares/query')
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data)
-    } else if (req.query.format === 'xml') {
-        res.send(xml("result", JSON.stringify({city: req.result.data})))
-    }
-    else {
-        res.json({
-            result: req.result.data
-        })
-    }
-}
-
-router.get('/', function(req, res, next) {
-    req.query = squel.select().from('municipios').limit(2).toParam()
-    next()
-}, query, response)
-
-router.get('/:id', function(req, res, next) {
-    req.query = squel.select().from('municipios').where('pk_municipio_id=?', parseInt(req.params.id, 10)).toParam()
-    next()
-}, query, response)
-
-router.get('/state/:id', function(req, res, next) {
-    req.query = squel.select().from('municipios').where('fk_estado_id=?', parseInt(req.params.id, 10)).toParam()
-    next()
-}, query, response)
-
-module.exports = router
diff --git a/src/libs/routes/city.js b/src/libs/routes/city.js
index 1b390498..95423602 100644
--- a/src/libs/routes/city.js
+++ b/src/libs/routes/city.js
@@ -1,70 +1,35 @@
 const express = require('express');
-
-const xml = require('js2xmlparser');
-
 const cityApp = express();
-
 const libs = `${process.cwd()}/libs`;
-
 const log = require(`${libs}/log`)(module);
-
+const squel = require('squel');
 const dbQuery = require(`${libs}/db/query_exec`);
+const query = require(`${libs}/middlewares/query`);
+const response = require(`${libs}/middlewares/response`);
 
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ city: req.result })));
-    } else {
-        res.json({ result: req.result });
-    }
-}
-
-cityApp.get('/', (req, res) => {
-    const citySql = 'SELECT * FROM municipios LIMIT 5';
-    dbQuery(yearSql).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
+cityApp.get('/', (req, res, next) => {
+    req.query = squel.select().from('municipios').toParam();
+    next();
+}, query, response);
 
-cityApp.get('/:id', (req, res) => {
-    const citySql = 'SELECT * FROM municipios WHERE pk_municipio_id = ?';
-    const cityId = parseInt(req.params.id, 10);
-    dbQuery(citySql, [cityId]).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
+cityApp.get('/:id', (req, res, next) => {
+    req.query = squel.select().from('municipios').where('pk_municipio_id = ?',
+        parseInt(req.params.id, 10)).toParam();
+    next();
+}, query, response);
 
-cityApp.get('/ibge/:id', (req, res) => {
+cityApp.get('/ibge/:id', (req, res, next) => {
     const citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
     const cityIbgeCode = req.params.id;
-    dbQuery(citySql, [cityIbgeCode]).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
-
-cityApp.get('/state/:id', (req, res) => {
-    const citySql = 'SELECT * FROM municipios WHERE fk_estado_id = ?';
-    const stateId = parseInt(req.params.id, 10);
-    dbQuery(citySql, [stateId]).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
+    req.query = squel.select().from('municipios').where('codigo_ibge = ?',
+        parseInt(req.params.id, 10)).toParam();
+    next();
+}, query, response);
+
+cityApp.get('/state/:id', (req, res, next) => {
+    req.query = squel.select().from('municipios').where('fk_estado_id = ?',
+        parseInt(req.params.id, 10));
+    next();
+}, query, response);
 
 module.exports = cityApp;
-- 
GitLab


From d960e22cb8297d2a8a9b2c2291318a327a7915ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:11:13 -0300
Subject: [PATCH 22/58] Integrate query middleware with query execution promise
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/middlewares/query.js | 23 +++++++++++------------
 1 file changed, 11 insertions(+), 12 deletions(-)

diff --git a/src/libs/middlewares/query.js b/src/libs/middlewares/query.js
index 80f00348..fb53c561 100644
--- a/src/libs/middlewares/query.js
+++ b/src/libs/middlewares/query.js
@@ -1,17 +1,16 @@
-var libs = process.cwd() + '/libs/'
-var conn = require(libs + 'db/monet')
-var log = require(libs + 'log')(module)
-var queryExec = require(libs + 'db/query_exec')
+const libs = process.cwd() + '/libs/';
+const log = require(libs + 'log')(module);
+const execQuery = require(libs + 'db/query_exec');
 
 function query(req, res, next) {
     log.debug(req.query)
-    queryExec(req.query.text, req.query.values)
-    .then(function(result) {
-        log.debug(result)
-        console.log("PEOPO");
-        req.result = result
-        next()
-    })
+    execQuery(req.query.text, req.query.values).then((result) => {
+        log.debug(result);
+        req.result = result;
+        next();
+    }, (error) => {
+        next(error);
+    });
 }
 
-module.exports = query
+module.exports = query;
-- 
GitLab


From d900861eae1f1908fd31d78fb53424d4eb976e1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:11:35 -0300
Subject: [PATCH 23/58] Add response middleware
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/middlewares/response.js | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 src/libs/middlewares/response.js

diff --git a/src/libs/middlewares/response.js b/src/libs/middlewares/response.js
new file mode 100644
index 00000000..8328b4ff
--- /dev/null
+++ b/src/libs/middlewares/response.js
@@ -0,0 +1,13 @@
+const xml = require('js2xmlparser');
+
+function response(req, res) {
+    if (req.query.format === 'csv') {
+        res.csv(req.result);
+    } else if (req.query.format === 'xml') {
+        res.send(xml('result', JSON.stringify({ city: req.result })));
+    } else {
+        res.json({ result: req.result });
+    }
+}
+
+module.exports = response;
-- 
GitLab


From f216ab9e5f2b66f741efef7ecfc0f432cf0c065b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:19:39 -0300
Subject: [PATCH 24/58] Clean up city.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/city.js | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/libs/routes/city.js b/src/libs/routes/city.js
index 95423602..af22a1b7 100644
--- a/src/libs/routes/city.js
+++ b/src/libs/routes/city.js
@@ -3,7 +3,6 @@ const cityApp = express();
 const libs = `${process.cwd()}/libs`;
 const log = require(`${libs}/log`)(module);
 const squel = require('squel');
-const dbQuery = require(`${libs}/db/query_exec`);
 const query = require(`${libs}/middlewares/query`);
 const response = require(`${libs}/middlewares/response`);
 
@@ -19,8 +18,6 @@ cityApp.get('/:id', (req, res, next) => {
 }, query, response);
 
 cityApp.get('/ibge/:id', (req, res, next) => {
-    const citySql = 'SELECT * FROM municipios WHERE codigo_ibge = ?';
-    const cityIbgeCode = req.params.id;
     req.query = squel.select().from('municipios').where('codigo_ibge = ?',
         parseInt(req.params.id, 10)).toParam();
     next();
-- 
GitLab


From 63bdc8ec8c7f16a18064cdde5d7be46b250afd27 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:20:00 -0300
Subject: [PATCH 25/58] Merge regions.js with region.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/region.js  | 54 ++++++++++----------------------------
 src/libs/routes/regions.js | 38 ---------------------------
 2 files changed, 14 insertions(+), 78 deletions(-)
 delete mode 100644 src/libs/routes/regions.js

diff --git a/src/libs/routes/region.js b/src/libs/routes/region.js
index 36b1f049..6461b3a4 100644
--- a/src/libs/routes/region.js
+++ b/src/libs/routes/region.js
@@ -1,46 +1,20 @@
 const express = require('express');
-
-const xml = require('js2xmlparser');
-
 const regionApp = express();
-
 const libs = `${process.cwd()}/libs`;
-
 const log = require(`${libs}/log`)(module);
-
-const dbQuery = require(`${libs}/db/query_exec`);
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ state: req.result })));
-    } else {
-        res.json({ result: req.result });
-    }
-}
-
-regionApp.get('/', (req, res) => {
-    const regionSql = 'SELECT * FROM regioes';
-    dbQuery(regionSql).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
-
-regionApp.get('/:id', (req, res) => {
-    const regionSql = 'SELECT * FROM regioes WHERE pk_regiao_id = ?';
-    const regionId = parseInt(req.params.id, 10);
-    dbQuery(regionSql, [regionId]).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
+const squel = require('squel');
+const query = require(`${libs}/middlewares/query`);
+const response = require(`${libs}/middlewares/response`);
+
+regionApp.get('/', (req, res, next) => {
+    req.query = squel.select().from('regioes').toParam();
+    next();
+}, query, response);
+
+regionApp.get('/:id', (req, res, next) => {
+    req.query = squel.select().from('regioes').where('pk_regiao_id = ?',
+        parseInt(req.params.id, 10)).toParam();
+    next();
+}, query, response);
 
 module.exports = regionApp;
diff --git a/src/libs/routes/regions.js b/src/libs/routes/regions.js
deleted file mode 100644
index cfbbcd94..00000000
--- a/src/libs/routes/regions.js
+++ /dev/null
@@ -1,38 +0,0 @@
-var express = require('express')
-var xml = require('js2xmlparser')
-var router = express.Router()
-var squel = require('squel')
-
-var libs = process.cwd() + '/libs/'
-
-var log = require(libs + 'log')(module)
-var config = require(libs + 'config')
-
-var conn = require(libs + 'db/monet')
-
-var query = require(libs + 'middlewares/query')
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data)
-    } else if (req.query.format === 'xml') {
-        res.send(xml("result", JSON.stringify({state: req.result.data})))
-    }
-    else {
-        res.json({
-            result: req.result.data
-        })
-    }
-}
-
-router.get('/', function(req, res, next) {
-    req.query = squel.select().from('regioes').toParam()
-    next()
-}, query, response)
-
-router.get('/:id', function(req, res, next) {
-    req.query = squel.select().from('regioes').where('pk_regiao_id=?', parseInt(req.params.id, 10)).toParam()
-    next()
-}, query, response)
-
-module.exports = router
-- 
GitLab


From bcf8b40dd1fdb65a4582c0bba5dd3724b295c2a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:25:56 -0300
Subject: [PATCH 26/58] Merge states.js with state.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/state.js  | 60 +++++++++------------------------------
 src/libs/routes/states.js | 44 ----------------------------
 2 files changed, 14 insertions(+), 90 deletions(-)
 delete mode 100644 src/libs/routes/states.js

diff --git a/src/libs/routes/state.js b/src/libs/routes/state.js
index a1fa64a5..c5493f20 100644
--- a/src/libs/routes/state.js
+++ b/src/libs/routes/state.js
@@ -1,58 +1,26 @@
 const express = require('express');
-
-const xml = require('js2xmlparser');
-
 const stateApp = express();
-
 const libs = `${process.cwd()}/libs`;
-
 const log = require(`${libs}/log`)(module);
-
-const dbQuery = require(`${libs}/db/query_exec`);
-
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data);
-    } else if (req.query.format === 'xml') {
-        res.send(xml('result', JSON.stringify({ state: req.result.data })));
-    } else {
-        res.json({ result: req.result.data });
-    }
-}
+const squel = require('squel');
+const query = require(`${libs}/middlewares/query`);
+const response = require(`${libs}/middlewares/response`);
 
 stateApp.get('/', (req, res, next) => {
-    const stateSql = 'SELECT * FROM estados';
-    dbQuery(stateSql).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
+    req.query = squel.select().from('estados').toParam();
+    next();
+}, query, response);
 
 stateApp.get('/:id', (req, res, next) => {
-    const stateSql = 'SELECT * FROM estados WHERE pk_estado_id = ?';
-    const stateId = parseInt(req.params.id, 10);
-    dbQuery(stateSql, [stateId]).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
+    req.query = squel.select().from('estados').where('pk_estado_id = ?',
+        parseInt(req.params.id, 10)).toParam();
+    next();
+}, query, response);
 
 stateApp.get('/region/:id', (req, res, next) => {
-    const stateSql = 'SELECT * FROM estados WHERE fk_regiao_id = ?';
-    const regionId = parseInt(req.params.id, 10);
-    dbQuery(stateSql, [regionId]).then((result) => {
-        req.result = result;
-        return response(req, res);
-    }, (error) => {
-        log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
-    });
-});
+    req.query = squel.select().from('estados').where('fk_regiao_id = ?',
+        parseInt(req.params.id, 10)).toParam();
+    next();
+}, query, response);
 
 module.exports = stateApp;
diff --git a/src/libs/routes/states.js b/src/libs/routes/states.js
deleted file mode 100644
index 79959ef8..00000000
--- a/src/libs/routes/states.js
+++ /dev/null
@@ -1,44 +0,0 @@
-var express = require('express')
-var xml = require('js2xmlparser')
-var router = express.Router()
-var squel = require('squel')
-
-var libs = process.cwd() + '/libs/'
-
-var log = require(libs + 'log')(module)
-var config = require(libs + 'config')
-
-var conn = require(libs + 'db/monet')
-
-var query = require(libs + 'middlewares/query')
-
-function response(req, res) {
-    console.log('respostas :)')
-    if (req.query.format === 'csv') {
-        res.csv(req.result.data)
-    } else if (req.query.format === 'xml') {
-        res.send(xml("result", JSON.stringify({state: req.result.data})))
-    }
-    else {
-        res.json({
-            result: req.result.data
-        })
-    }
-}
-
-router.get('/', function(req, res, next) {
-    req.query = squel.select().from('estados').toParam()
-    next()
-}, query, response)
-
-router.get('/:id', function(req, res, next) {
-    req.query = squel.select().from('estados').where('pk_estado_id = ?', parseInt(req.params.id, 10)).toParam()
-    next()
-}, query, response)
-
-router.get('/region/:id', function(req, res, next) {
-    req.query = squel.select().from('estados').where('fk_regiao_id = ?', parseInt(req.params.id, 10)).toParam()
-    next()
-}, query, response)
-
-module.exports = router
-- 
GitLab


From 818591bac1315f39b033836ae414cefd05314041 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:26:58 -0300
Subject: [PATCH 27/58] Change default host to simcaqdb1 in config.json
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 config.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/config.json b/config.json
index 21862d34..23b96cb1 100644
--- a/config.json
+++ b/config.json
@@ -1,7 +1,7 @@
 {
     "port": 3000,
     "monetdb": {
-        "host": "localhost",
+        "host": "simcaqdb1",
         "port": 50000,
         "dbname": "simcaq_dev",
         "user": "monetdb",
-- 
GitLab


From d905b17fb648c14f9e120e1b36b16c6f6ee7c3df Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:28:30 -0300
Subject: [PATCH 28/58] Add skeleton for basic dev tasks in gulpfile
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 gulpfile.babel.js | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/gulpfile.babel.js b/gulpfile.babel.js
index e07481f9..a40a9cac 100644
--- a/gulpfile.babel.js
+++ b/gulpfile.babel.js
@@ -2,7 +2,10 @@ const gulp = require('gulp');
 const babel = require('gulp-babel');
 const eslint = require('gulp-eslint');
 
-gulp.task('default', () => {
+/**
+ * Compile source files
+ */
+function compile() {
     // run ESLint
     gulp.src('src/**/*.js')
         .pipe(eslint())
@@ -12,4 +15,23 @@ gulp.task('default', () => {
     gulp.src('src/**/*.js')
         .pipe(babel())
         .pipe(gulp.dest('build'));
+
+    // copy configuration file to build directory
+    gulp.src('config.json')
+        .pipe(gulp.dest('build'));
+
+}
+
+gulp.task('build', compile);
+
+gulp.task('run', ['build'], () => {
 });
+
+gulp.task('watch', [], () => {
+    compile();
+});
+
+gulp.task('test', ['build'], () => {
+});
+
+gulp.task('default', ['watch']);
-- 
GitLab


From 3956a0b1a37b8201acbe07baea3acf0eb07a7b7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:29:19 -0300
Subject: [PATCH 29/58] Clean up enrollment directory
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/enrollment/all.js     |  0
 src/libs/enrollment/city.js    |  0
 src/libs/enrollment/common.js  | 14 --------------
 src/libs/enrollment/country.js |  0
 src/libs/enrollment/region.js  |  0
 src/libs/enrollment/state.js   |  0
 6 files changed, 14 deletions(-)
 delete mode 100644 src/libs/enrollment/all.js
 delete mode 100644 src/libs/enrollment/city.js
 delete mode 100644 src/libs/enrollment/common.js
 delete mode 100644 src/libs/enrollment/country.js
 delete mode 100644 src/libs/enrollment/region.js
 delete mode 100644 src/libs/enrollment/state.js

diff --git a/src/libs/enrollment/all.js b/src/libs/enrollment/all.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/libs/enrollment/city.js b/src/libs/enrollment/city.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/libs/enrollment/common.js b/src/libs/enrollment/common.js
deleted file mode 100644
index 207f44c3..00000000
--- a/src/libs/enrollment/common.js
+++ /dev/null
@@ -1,14 +0,0 @@
-const libs = `${process.cwd()}/libs`;
-
-const log = require(`${libs}/log`)(module);
-
-const sqlDecorator = require(`${libs}/query_decorator`);
-
-const yearRange = () => {
-    const yearSql = 'SELECT MIN(t.ano_censo) AS start_year, MAX(t.ano_censo)'
-        + 'AS end_year FROM turmas AS t';
-    log.debug('Generated SQL query for enrollments\' year range');
-    return sqlDecorator.execQuery(yearSql, []);
-};
-
-module.exports = yearRange;
diff --git a/src/libs/enrollment/country.js b/src/libs/enrollment/country.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/libs/enrollment/region.js b/src/libs/enrollment/region.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/libs/enrollment/state.js b/src/libs/enrollment/state.js
deleted file mode 100644
index e69de29b..00000000
-- 
GitLab


From 91f8d6533ad1190d823dae8df9e22f553ba8b406 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:42:41 -0300
Subject: [PATCH 30/58] Add dependencies to README
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 README.md | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/README.md b/README.md
index 0c363720..181434b0 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,24 @@
 # SIMCAQ
+
+# Dependencies
+
+Previous versions of Node.js do not support ECMAScript6, it is recommended to use at least version 4.5.0LTS.
+
+1) Install [NVM (Node Version Manager)](https://github.com/creationix/nvm)
+
+2) Install Node.js via NVM
+
+> source ~/.bashrc
+> nvm install v4.5.0
+
+3) Enable Node.js
+
+> nvm use v4.5.0
+
+4) Install babel and gulp globally
+
+> npm install -g gulp gulp-cli babel
+
+5) Install project dependencies
+
+> npm install
-- 
GitLab


From ae07f0e8ad004dee5bba6039a4dd98b9f967d6d2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:47:59 -0300
Subject: [PATCH 31/58] Remove test route from api.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/api.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js
index 64c84519..aa4637f5 100644
--- a/src/libs/routes/api.js
+++ b/src/libs/routes/api.js
@@ -15,6 +15,5 @@ api.use('/v1/enrollment', enrollment);
 api.use('/v1/state',      state);
 api.use('/v1/region',     region);
 api.use('/v1/city',       city);
-api.use('/v1/test', test);
 
 module.exports = api;
-- 
GitLab


From 4825f0be58c061ba24923ce4ac2634984b39c52d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:48:19 -0300
Subject: [PATCH 32/58] Remove test directory from repo root directory
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 test/test.js | 142 ---------------------------------------------------
 1 file changed, 142 deletions(-)
 delete mode 100644 test/test.js

diff --git a/test/test.js b/test/test.js
deleted file mode 100644
index d6b67ff8..00000000
--- a/test/test.js
+++ /dev/null
@@ -1,142 +0,0 @@
-var chai = require('chai');
-var chaiHttp = require('chai-http');
-var assert = chai.assert;
-var expect = chai.expect;
-var should = chai.should(); //actually call the function
-var server = require('../libs/app');
-
-chai.use(chaiHttp);
-
-describe('request enrollments', function(){
-
-  it('should list enrollments', function(done){
-    chai.request(server)
-      .get('/api/v1/enrollments')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('name');
-        res.body.result[0].should.have.property('total');
-        done();
-      })
-  });
-});
-
-describe('request regions', function(){
-
-  it('should list all regions', function(done){
-    chai.request(server)
-      .get('/api/v1/regions')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-
-  it('should list region by id', function(done){
-    chai.request(server)
-      .get('/api/v1/regions/1')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result.should.have.length(1);
-        res.body.result[0].should.have.property('pk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-});
-
-describe('request states', function(){
-
-  it('should list all states', function(done){
-    chai.request(server)
-      .get('/api/v1/states')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_estado_id');
-        res.body.result[0].should.have.property('fk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-
-  it('should list a state by id', function(done){
-    chai.request(server)
-      .get('/api/v1/states/11')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result.should.have.length(1);
-        res.body.result[0].should.have.property('pk_estado_id');
-        res.body.result[0].should.have.property('fk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-
-  it('should list states by region id', function(done){
-    chai.request(server)
-      .get('/api/v1/states/region/1')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_estado_id');
-        res.body.result[0].should.have.property('fk_regiao_id');
-        res.body.result[0].should.have.property('nome');
-        done();
-      })
-  });
-});
-
-describe('request cities', function(){
-
-  it('should list all cities', function(done){
-    chai.request(server)
-      .get('/api/v1/cities')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_municipio_id');
-        res.body.result[0].should.have.property('fk_estado_id');
-        res.body.result[0].should.have.property('nome');
-        res.body.result[0].should.have.property('codigo_ibge');
-        done();
-      })
-  });
-
-  it('should list a city by id', function(done){
-    chai.request(server)
-      .get('/api/v1/cities/1')
-      .end(function(err, res){
-        res.should.have.status(200);
-        res.should.be.json;
-        res.body.should.have.property('result');
-        res.body.result.should.be.a('array');
-        res.body.result[0].should.have.property('pk_municipio_id');
-        res.body.result[0].should.have.property('fk_estado_id');
-        res.body.result[0].should.have.property('nome');
-        res.body.result[0].should.have.property('codigo_ibge');
-        done();
-      })
-  });
-
-});
-- 
GitLab


From 0b0f1646ee55b6a0f43c6aeffaf0c6f39cfc84e6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 13:55:10 -0300
Subject: [PATCH 33/58] Fix ESLint errors in enrollment.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/enrollment.js | 32 ++++++++++++++------------------
 1 file changed, 14 insertions(+), 18 deletions(-)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 131ab7ae..91fbaeae 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -1,7 +1,5 @@
 const express = require('express');
 
-const xml = require('js2xmlparser');
-
 const enrollmentApp = express();
 
 const libs = `${process.cwd()}/libs`;
@@ -10,15 +8,8 @@ const log = require(`${libs}/log`)(module);
 
 const dbQuery = require('../db/query_exec');
 
-function response(req, res) {
-    if (req.query.format === 'csv') {
-        return res.csv(req.result);
-    } else if (req.query.format === 'xml') {
-        return res.send(xml('result', JSON.stringify({ state: req.result })));
-    } else {
-        return res.json({ result: req.result });
-    }
-}
+const response = require('../middlewares/response');
+
 /**
  * Complete range of the enrollments dataset
  *
@@ -35,11 +26,12 @@ enrollmentApp.get('/year_range', (req, res, next) => {
         return response(req, res);
     }, (error) => {
         log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+        next('Internal error, request could not be satisfied at this moment. Please, '
+            + 'try again later');
     });
 });
 
-enrollmentApp.get('/education_level', (req, res) => {
+enrollmentApp.get('/education_level', (req, res, next) => {
     const edLevelSql = 'SELECT pk_etapa_ensino_id AS id, desc_etapa AS '
         + 'education_level FROM etapa_ensino';
 
@@ -48,18 +40,20 @@ enrollmentApp.get('/education_level', (req, res) => {
         return response(req, res);
     }, (error) => {
         log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+        next('Internal error, request could not be satisfied at this moment. Please, '
+            + 'try again later');
     });
 });
 
-enrollmentApp.get('/data', (req, res) => {
+enrollmentApp.get('/data', (req, res, next) => {
     const schoolClassSql = 'SELECT * FROM turmas';
     dbQuery(schoolClassSql).then((result) => {
         req.result = result;
         return response(req, res);
     }, (error) => {
         log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+        next('Internal error, request could not be satisfied at this moment. Please, '
+            + 'try again later');
     });
 });
 
@@ -288,7 +282,8 @@ enrollmentApp.get('/enrollments', (req, res, next) => {
         // '/enrollments' route, since when no +aggregate+ parameter is given,
         // it defaults to use the query for the whole country.
         log.error('BUG -- No SQL query was found to be executed!');
-        next('Internal error, request could not be satisfied at this moment. Please, try again later');
+        next('Internal error, request could not be satisfied at this moment. Please, '
+            + 'try again later');
     } else {
         log.debug('SQL query: ${ req.sqlQuery }?');
         log.debug(req.sqlQuery);
@@ -297,7 +292,8 @@ enrollmentApp.get('/enrollments', (req, res, next) => {
             return response(req, res);
         }, (error) => {
             log.error(`[${req.originalUrl}] SQL query error: ${error}`);
-            next('Internal error, request could not be satisfied at this moment. Please, try again later');
+            next('Internal error, request could not be satisfied at this moment. Please, '
+                + 'try again later');
         });
     }
 });
-- 
GitLab


From 77196f5ec82153e5ce7c3272e238e06fd9b38439 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:26:07 -0300
Subject: [PATCH 34/58] Fix error in city ibge route
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/city.js | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/libs/routes/city.js b/src/libs/routes/city.js
index af22a1b7..aa28900b 100644
--- a/src/libs/routes/city.js
+++ b/src/libs/routes/city.js
@@ -1,9 +1,13 @@
 const express = require('express');
+
 const cityApp = express();
+
 const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
+
 const squel = require('squel');
+
 const query = require(`${libs}/middlewares/query`);
+
 const response = require(`${libs}/middlewares/response`);
 
 cityApp.get('/', (req, res, next) => {
@@ -19,7 +23,7 @@ cityApp.get('/:id', (req, res, next) => {
 
 cityApp.get('/ibge/:id', (req, res, next) => {
     req.query = squel.select().from('municipios').where('codigo_ibge = ?',
-        parseInt(req.params.id, 10)).toParam();
+        req.params.id).toParam();
     next();
 }, query, response);
 
-- 
GitLab


From 24814ec5b9b195384f16ed24ce35ecea273b690e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:26:23 -0300
Subject: [PATCH 35/58] Add missing project dependencies in README
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 181434b0..00e89a48 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ Previous versions of Node.js do not support ECMAScript6, it is recommended to us
 
 4) Install babel and gulp globally
 
-> npm install -g gulp gulp-cli babel
+> npm install -g gulp gulp-cli babel babel-cli babel-core babel-register mocha
 
 5) Install project dependencies
 
-- 
GitLab


From 5c5ccac781f2eba9c865a7d55c5270f6fbf498b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:26:47 -0300
Subject: [PATCH 36/58] Move mocha, chai and chai-http from devDependencies to
 dependencies
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 package.json | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/package.json b/package.json
index 8739650a..abc0c116 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,8 @@
   "dependencies": {
     "apicache": "0.0.14",
     "body-parser": "^1.13.1",
+    "chai": "^3.5.0",
+    "chai-http": "^3.0.0",
     "cookie-parser": "^1.3.5",
     "cors": "^2.7.1",
     "csv-express": "^1.1.0",
@@ -20,6 +22,7 @@
     "forever": "^0.15.2",
     "js2xmlparser": "^1.0.0",
     "method-override": "^2.3.3",
+    "mocha": "^2.5.3",
     "monetdb-pool": "0.0.8",
     "nconf": "^0.6.x",
     "squel": "^5.4.2",
@@ -33,8 +36,6 @@
     "babel-register": "^6.14.0",
     "babelify": "^7.3.0",
     "browserify": "^13.1.0",
-    "chai": "^3.5.0",
-    "chai-http": "^3.0.0",
     "eslint": "^3.3.1",
     "eslint-config-airbnb": "^10.0.1",
     "eslint-plugin-import": "^1.13.0",
@@ -43,7 +44,6 @@
     "gulp": "^3.9.1",
     "gulp-babel": "^6.1.2",
     "gulp-cli": "^1.2.2",
-    "gulp-eslint": "^3.0.1",
-    "mocha": "^2.5.3"
+    "gulp-eslint": "^3.0.1"
   }
 }
-- 
GitLab


From 38ac008f42747d342e5abe3328eeab37df7b5ffc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:27:16 -0300
Subject: [PATCH 37/58] Fix linting errors in query_exec.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/db/query_exec.js | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/libs/db/query_exec.js b/src/libs/db/query_exec.js
index c1464f28..bd921d1d 100644
--- a/src/libs/db/query_exec.js
+++ b/src/libs/db/query_exec.js
@@ -1,17 +1,19 @@
 const libs = `${process.cwd()}/libs`;
+
 const log = require(`${libs}/log`)(module);
+
 const conn = require(`${libs}/db/monet`);
 
 /**
  * Promise that executes an SQL query with optional parameters
  *
  * Examples:
- *     Query with no parameters:
- *         execSqlQuery('SELECT * FROM people');
- *     Query with one parameter:
- *         execSqlQuery('SELECT name, age FROM people WHERE id = ?', [1]);
- *     Query with more than one parameter:
- *         execSqlQuery('SELECT name, age FROM people WHERE city = ? AND age > ?', ['São Paulo', 35]);
+ *   Query with no parameters:
+ *     execSqlQuery('SELECT * FROM people');
+ *   Query with one parameter:
+ *     execSqlQuery('SELECT name, age FROM people WHERE id = ?', [1]);
+ *   Query with more than one parameter:
+ *     execSqlQuery('SELECT name, age FROM people WHERE city = ? AND age > ?', ['São Paulo', 35]);
  *
  * @param {string} sqlQuery       - SQL query to be executed by the Promise
  * @param {array}  sqlQueryParams - SQL query parameters
@@ -28,7 +30,7 @@ function execSqlQuery(sqlQuery, sqlQueryParams = []) {
                         resolve(dbResult.data);
                     },
                     (dbError) => {
-                        log.error(`SQL query execution error: ${error.message}`);
+                        log.error(`SQL query execution error: ${dbError.message}`);
                         reject(new Error(dbError.message));
                     }
                 );
-- 
GitLab


From 4d4e138b2309cd8bfc2eab958170212ff656f054 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:27:39 -0300
Subject: [PATCH 38/58] Rewrite dimensions middleware to ES6
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/middlewares/dimensions.js | 48 ++++++++++++++++--------------
 1 file changed, 26 insertions(+), 22 deletions(-)

diff --git a/src/libs/middlewares/dimensions.js b/src/libs/middlewares/dimensions.js
index c17e4a6d..8532425c 100644
--- a/src/libs/middlewares/dimensions.js
+++ b/src/libs/middlewares/dimensions.js
@@ -13,36 +13,40 @@
 */
 
 function intersect(a, b) {
-    var t
-    if (b.length > a.length) t = b, b = a, a = t
-    return a.filter(function (e) {
-        if (b.indexOf(e) !== -1) return true
-    })
+    let t;
+    if (b.length > a.length) {
+        t = b; b = a; a = t;
+    }
+    return a.filter((e) => b.indexOf(e) !== -1);
 }
 
 function dimensions(dims) {
-    return function(req, res, next) {
-        req.dims = {}
-        if(req.query.dims) {
-            var params = req.query.dims.split(",")
-            var dimObj = {}
-            for(var i=0; i<params.length; ++i) {
-                var kv = params[i].split(":")
-                dimObj[kv[0]] = (typeof kv[1] === 'undefined') ? null : kv[1]
+    return (req, res, next) => {
+        req.dims = {};
+        if (req.query.dims) {
+            const params = req.query.dims.split(',');
+            const dimObj = {};
+            for (const param of params) {
+                const kv = param.split(':');
+                dimObj[kv[0]] = (typeof kv[1] === 'undefined') ? null : kv[1];
             }
+            // for(let i=0; i<params.length; ++i) {
+            //     let kv = params[i].split(':');
+            //     dimObj[kv[0]] = (typeof kv[1] === 'undefined') ? null : kv[1];
+            // }
+
             // If the dims array exists and is not empty
-            if(typeof dims !== 'undefined' && dims.length > 0) {
-                var intersection = intersect(dims, Object.keys(dimObj))
-                for(var i=0; i<intersection.length; ++i) {
-                    req.dims[intersection[i]] = dimObj[intersection[i]]
+            if (typeof dims !== 'undefined' && dims.length > 0) {
+                const intersection = intersect(dims, Object.keys(dimObj));
+                for (let i = 0; i < intersection.length; ++i) {
+                    req.dims[intersection[i]] = dimObj[intersection[i]];
                 }
             } else {
-                req.dims = dimObj
+                req.dims = dimObj;
             }
         }
-        console.log(req.dims)
-        next()
-    }
+        next();
+    };
 }
 
-module.exports = dimensions
+module.exports = dimensions;
-- 
GitLab


From 614d5b05ac0dd8362598729f25b8f44e7d2c63cb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:27:58 -0300
Subject: [PATCH 39/58] Fix linting errors in query middleware
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/middlewares/query.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/libs/middlewares/query.js b/src/libs/middlewares/query.js
index fb53c561..6b91fc8f 100644
--- a/src/libs/middlewares/query.js
+++ b/src/libs/middlewares/query.js
@@ -1,9 +1,9 @@
-const libs = process.cwd() + '/libs/';
-const log = require(libs + 'log')(module);
-const execQuery = require(libs + 'db/query_exec');
+const libs = `${process.cwd()}/libs`;
+const log = require(`${libs}/log`)(module);
+const execQuery = require(`${libs}/db/query_exec`);
 
 function query(req, res, next) {
-    log.debug(req.query)
+    log.debug(req.query);
     execQuery(req.query.text, req.query.values).then((result) => {
         log.debug(result);
         req.result = result;
-- 
GitLab


From 9864daf256237ede3c7747380363640b2f144525 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:28:14 -0300
Subject: [PATCH 40/58] Fix linting errors in api module
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/api.js | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js
index aa4637f5..9a23be8c 100644
--- a/src/libs/routes/api.js
+++ b/src/libs/routes/api.js
@@ -1,10 +1,14 @@
 const express = require('express');
+
 const api = express();
+
 const enrollment = require('./enrollment');
+
 const state = require('./state');
+
 const region = require('./region');
+
 const city = require('./city');
-const test = require('./cities');
 
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API is running' });
@@ -12,8 +16,8 @@ api.get('/', (req, res) => {
 
 // mount API routes
 api.use('/v1/enrollment', enrollment);
-api.use('/v1/state',      state);
-api.use('/v1/region',     region);
-api.use('/v1/city',       city);
+api.use('/v1/state', state);
+api.use('/v1/region', region);
+api.use('/v1/city', city);
 
 module.exports = api;
-- 
GitLab


From ba6d275ab5de34b46bba257924f67168060c4946 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:28:39 -0300
Subject: [PATCH 41/58] Remove unused log module from region
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/region.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/libs/routes/region.js b/src/libs/routes/region.js
index 6461b3a4..56c4cd2e 100644
--- a/src/libs/routes/region.js
+++ b/src/libs/routes/region.js
@@ -1,9 +1,13 @@
 const express = require('express');
+
 const regionApp = express();
+
 const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
+
 const squel = require('squel');
+
 const query = require(`${libs}/middlewares/query`);
+
 const response = require(`${libs}/middlewares/response`);
 
 regionApp.get('/', (req, res, next) => {
-- 
GitLab


From e60f3f3936ad4e84c4fdfa8a9203f936e82dcd79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:29:02 -0300
Subject: [PATCH 42/58] Remove unused log module from state
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/state.js | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/libs/routes/state.js b/src/libs/routes/state.js
index c5493f20..dae0eb71 100644
--- a/src/libs/routes/state.js
+++ b/src/libs/routes/state.js
@@ -1,9 +1,13 @@
 const express = require('express');
+
 const stateApp = express();
+
 const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
+
 const squel = require('squel');
+
 const query = require(`${libs}/middlewares/query`);
+
 const response = require(`${libs}/middlewares/response`);
 
 stateApp.get('/', (req, res, next) => {
-- 
GitLab


From 4efb6126b4b1554ec45eafb80494f8aac1a5cef7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:29:22 -0300
Subject: [PATCH 43/58] Fix routes in the API test
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/test/test.js | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/src/test/test.js b/src/test/test.js
index f9fec313..58e774f8 100644
--- a/src/test/test.js
+++ b/src/test/test.js
@@ -1,8 +1,13 @@
 const chai = require('chai');
+
 const chaiHttp = require('chai-http');
+
 const assert = chai.assert;
+
 const expect = chai.expect;
+
 const should = chai.should(); // actually call the function
+
 const server = require('../libs/app');
 
 chai.use(chaiHttp);
@@ -10,7 +15,7 @@ chai.use(chaiHttp);
 describe('request enrollments', () => {
     it('should list enrollments', (done) => {
         chai.request(server)
-            .get('/v1/enrollments')
+            .get('/v1/enrollment')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -26,7 +31,7 @@ describe('request enrollments', () => {
 describe('request regions', () => {
     it('should list all regions', (done) => {
         chai.request(server)
-            .get('/v1/regions')
+            .get('/v1/region')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -40,7 +45,7 @@ describe('request regions', () => {
 
     it('should list region by id', (done) => {
         chai.request(server)
-            .get('/v1/regions/1')
+            .get('/v1/region/1')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -55,10 +60,9 @@ describe('request regions', () => {
 });
 
 describe('request states', () => {
-
     it('should list all states', (done) => {
         chai.request(server)
-            .get('/v1/states')
+            .get('/v1/state')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -73,7 +77,7 @@ describe('request states', () => {
 
     it('should list a state by id', (done) => {
         chai.request(server)
-            .get('/v1/states/11')
+            .get('/v1/state/11')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -89,7 +93,7 @@ describe('request states', () => {
 
     it('should list states by region id', (done) => {
         chai.request(server)
-            .get('/v1/states/region/1')
+            .get('/v1/state/region/1')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -104,10 +108,9 @@ describe('request states', () => {
 });
 
 describe('request cities', () => {
-
     it('should list all cities', (done) => {
         chai.request(server)
-            .get('/v1/cities')
+            .get('/v1/city')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -123,7 +126,7 @@ describe('request cities', () => {
 
     it('should list a city by id', (done) => {
         chai.request(server)
-            .get('/v1/cities/1')
+            .get('/v1/city/1')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
@@ -139,7 +142,7 @@ describe('request cities', () => {
 
     it('should list a city by codigo_ibge', (done) => {
         chai.request(server)
-            .get('/v1/cities/ibge/1200013')
+            .get('/v1/city/ibge/1200013')
             .end((err, res) => {
                 res.should.have.status(200);
                 res.should.be.json;
-- 
GitLab


From bd67bf4dbbeadbeca9e31e8fb31dfb3656119a15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:34:15 -0300
Subject: [PATCH 44/58] Fix missing line break in README
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 README.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/README.md b/README.md
index 00e89a48..41202e14 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ Previous versions of Node.js do not support ECMAScript6, it is recommended to us
 2) Install Node.js via NVM
 
 > source ~/.bashrc
+
 > nvm install v4.5.0
 
 3) Enable Node.js
-- 
GitLab


From 92feed4f8ba6656c64298d0c1cdc637b7f1fc803 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Fri, 2 Sep 2016 14:37:53 -0300
Subject: [PATCH 45/58] Remove server.js from project root
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 server.js | 13 -------------
 1 file changed, 13 deletions(-)
 delete mode 100644 server.js

diff --git a/server.js b/server.js
deleted file mode 100644
index f27cd5c8..00000000
--- a/server.js
+++ /dev/null
@@ -1,13 +0,0 @@
-var debug = require('debug')('node-express-base')
-
-var libs = process.cwd() + '/libs/'
-var config = require(libs + 'config')
-var log = require(libs + 'log')(module)
-var app = require(libs + 'app')
-
-app.set('port', process.env.PORT || config.get('port') || 3000)
-
-var server = app.listen(app.get('port'), function() {
-  debug('Express server listening on port ' + server.address().port)
-  log.info('Express server listening on port ' + server.address().port)
-})
-- 
GitLab


From 1a870decc4bd3e6f300ad45d384f03aee0461acd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 09:17:49 -0300
Subject: [PATCH 46/58] Update GitLab CI to run tests
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 .gitlab-ci.yml | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1a6d95ee..12a95c40 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,13 +2,14 @@ stages:
   - test
 
 before_script:
+  - npm install --global gulp gulp-cli babel babel-cli babel-core babel-register mocha
   - npm install
-  - npm install --global gulp-cli
-  - npm install --global mocha
 
 run_tests:
   stage: test
   script:
-    - gulp && cd build/ && mocha
+    - gulp
+    - cd build
+    - mocha
   tags:
     - node
-- 
GitLab


From b886abd59c4adc22b99eb8fdc856a1c4b21e0a5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 09:50:56 -0300
Subject: [PATCH 47/58] Fix npm start and test commands
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 package.json | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/package.json b/package.json
index abc0c116..713f494e 100644
--- a/package.json
+++ b/package.json
@@ -5,8 +5,8 @@
   "description": "Simulador custo aluno-qualidade",
   "private": true,
   "scripts": {
-    "start": "forever start server.js || node server.js",
-    "test": "mocha"
+    "start": "cd build && forever start server.js || node server.js",
+    "test": "cd build && mocha"
   },
   "dependencies": {
     "apicache": "0.0.14",
-- 
GitLab


From 1d6aa4e3be7f3e85970efe3d5cdb0352d5671c24 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 09:51:31 -0300
Subject: [PATCH 48/58] Fix enrollment route to use /v1/enrollment
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/libs/routes/enrollment.js | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/libs/routes/enrollment.js b/src/libs/routes/enrollment.js
index 91fbaeae..7ca2be3e 100644
--- a/src/libs/routes/enrollment.js
+++ b/src/libs/routes/enrollment.js
@@ -57,7 +57,7 @@ enrollmentApp.get('/data', (req, res, next) => {
     });
 });
 
-enrollmentApp.use('/enrollments', (req, res, next) => {
+enrollmentApp.use('/', (req, res, next) => {
     const params = req.query;
     req.paramCnt = 0;
 
@@ -89,7 +89,7 @@ enrollmentApp.use('/enrollments', (req, res, next) => {
     next();
 });
 
-enrollmentApp.use('/enrollments', (req, res, next) => {
+enrollmentApp.use('/', (req, res, next) => {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'region') {
         log.debug('Using enrollments query for regions');
@@ -136,7 +136,7 @@ enrollmentApp.use('/enrollments', (req, res, next) => {
     next();
 });
 
-enrollmentApp.use('/enrollments', (req, res, next) => {
+enrollmentApp.use('/', (req, res, next) => {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'state') {
         log.debug('Using enrollments query for states');
@@ -184,7 +184,7 @@ enrollmentApp.use('/enrollments', (req, res, next) => {
     next();
 });
 
-enrollmentApp.use('/enrollments', (req, res, next) => {
+enrollmentApp.use('/', (req, res, next) => {
     const params = req.query;
     if (typeof params.aggregate !== 'undefined' && params.aggregate === 'city') {
         log.debug('Using enrollments query for cities');
@@ -231,7 +231,7 @@ enrollmentApp.use('/enrollments', (req, res, next) => {
     next();
 });
 
-enrollmentApp.use('/enrollments', (req, res, next) => {
+enrollmentApp.use('/', (req, res, next) => {
     const params = req.query;
     if (typeof params.aggregate === 'undefined') {
         log.debug('Using enrollments query for the whole country');
@@ -275,7 +275,7 @@ enrollmentApp.use('/enrollments', (req, res, next) => {
     next();
 });
 
-enrollmentApp.get('/enrollments', (req, res, next) => {
+enrollmentApp.get('/', (req, res, next) => {
     log.debug(`Request parameters: ${req}`);
     if (typeof req.sqlQuery === 'undefined') {
         // Should only happen if there is a bug in the chaining of the
-- 
GitLab


From a7a2123e7343710bc4d483a52d71cdd7239949a8 Mon Sep 17 00:00:00 2001
From: Lucas Gabriel Lima <lgl15@inf.ufpr.br>
Date: Mon, 5 Sep 2016 09:56:36 -0300
Subject: [PATCH 49/58] add nodemon task

---
 gulpfile.js  | 9 ++++++++-
 package.json | 1 -
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/gulpfile.js b/gulpfile.js
index 4274caa8..2c273dc1 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,6 +1,5 @@
 var gulp = require('gulp');
 var mocha = require('gulp-mocha');
-var browserSync = require('browser-sync');
 var nodemon = require('gulp-nodemon');
 
 
@@ -18,3 +17,11 @@ gulp.task('test', function(){
       process.exit();
     })
 });
+
+gulp.task('run', function () {
+  nodemon({
+    script: 'server.js',
+    ext: 'js html',
+    env: { 'NODE_ENV': 'development' }
+  })
+})
diff --git a/package.json b/package.json
index c7f218a2..c09906f2 100644
--- a/package.json
+++ b/package.json
@@ -26,7 +26,6 @@
   },
   "license": "MIT",
   "devDependencies": {
-    "browser-sync": "^2.14.3",
     "chai": "^3.5.0",
     "chai-http": "^3.0.0",
     "gulp": "^3.9.1",
-- 
GitLab


From edb1bc09e64093bce71614014262da1d5cd41bba Mon Sep 17 00:00:00 2001
From: Lucas Gabriel Lima <lgl15@inf.ufpr.br>
Date: Mon, 5 Sep 2016 10:05:46 -0300
Subject: [PATCH 50/58] add compile function gulpfile and nodemon task

---
 gulpfile.js  | 35 +++++++++++++++++++++++++++++++----
 package.json |  2 ++
 2 files changed, 33 insertions(+), 4 deletions(-)

diff --git a/gulpfile.js b/gulpfile.js
index 2c273dc1..8f45a9a0 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,9 +1,31 @@
-var gulp = require('gulp');
-var mocha = require('gulp-mocha');
-var nodemon = require('gulp-nodemon');
+const gulp = require('gulp');
+const mocha = require('gulp-mocha');
+const nodemon = require('gulp-nodemon');
+const babel = require('gulp-babel');
+const eslint = require('gulp-eslint');
 
 
-gulp.task('default', ['browser-sync'], function() {
+/**
+ * Compile source files
+ */
+function compile() {
+    // run ESLint
+    gulp.src('src/**/*.js')
+        .pipe(eslint())
+        .pipe(eslint.format());
+
+    // compile source to ES5
+    gulp.src('src/**/*.js')
+        .pipe(babel())
+        .pipe(gulp.dest('build'));
+
+    // copy configuration file to build directory
+    gulp.src('config.json')
+        .pipe(gulp.dest('build'));
+
+}
+
+gulp.task('default', function() {
 
 });
 
@@ -18,9 +40,14 @@ gulp.task('test', function(){
     })
 });
 
+gulp.task('watch', [], () => {
+    compile();
+});
+
 gulp.task('run', function () {
   nodemon({
     script: 'server.js',
+    tasks: ['watch'],
     ext: 'js html',
     env: { 'NODE_ENV': 'development' }
   })
diff --git a/package.json b/package.json
index c09906f2..85051562 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,8 @@
     "chai": "^3.5.0",
     "chai-http": "^3.0.0",
     "gulp": "^3.9.1",
+    "gulp-babel": "^6.1.2",
+    "gulp-eslint": "^3.0.1",
     "gulp-mocha": "^3.0.1",
     "gulp-nodemon": "^2.1.0",
     "gulp-plumber": "^1.1.0",
-- 
GitLab


From b9fe28436befb218217ec3bfe15e8495abefa34b Mon Sep 17 00:00:00 2001
From: Lucas Gabriel Lima <lgl15@inf.ufpr.br>
Date: Mon, 5 Sep 2016 10:14:20 -0300
Subject: [PATCH 51/58] add ignored files to nodemon watch

---
 gulpfile.js | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/gulpfile.js b/gulpfile.js
index 8f45a9a0..b9b39bbc 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -25,17 +25,15 @@ function compile() {
 
 }
 
-gulp.task('default', function() {
+gulp.task('default', ['run']);
 
-});
-
-gulp.task('test', function(){
+gulp.task('test', () => {
   gulp.src('test/test.js', {read: false})
     .pipe(mocha())
-    .once('error', function(){
+    .once('error', () => {
       process.exit(1);
     })
-    .once('end', function(){
+    .once('end', () => {
       process.exit();
     })
 });
@@ -44,10 +42,11 @@ gulp.task('watch', [], () => {
     compile();
 });
 
-gulp.task('run', function () {
+gulp.task('run', () => {
   nodemon({
     script: 'server.js',
     tasks: ['watch'],
+    ignore: ["test/test.js", "gulpfile.js"],
     ext: 'js html',
     env: { 'NODE_ENV': 'development' }
   })
-- 
GitLab


From ebcb4741cd592586ac9102a5c4a3d9541f9d69b3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 10:15:55 -0300
Subject: [PATCH 52/58] Add debug flag to disable debug logging
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 config.json     |  1 +
 src/libs/log.js | 12 ++++++++++--
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/config.json b/config.json
index 23b96cb1..0574d6e9 100644
--- a/config.json
+++ b/config.json
@@ -1,5 +1,6 @@
 {
     "port": 3000,
+    "debug" : false,
     "monetdb": {
         "host": "simcaqdb1",
         "port": 50000,
diff --git a/src/libs/log.js b/src/libs/log.js
index 0eb64119..ecf6df02 100644
--- a/src/libs/log.js
+++ b/src/libs/log.js
@@ -1,3 +1,5 @@
+const config = require('./config');
+
 const winston = require('winston');
 
 winston.emitErrs = true;
@@ -8,9 +10,10 @@ function getFilePath(module) {
 }
 
 function logger(module) {
-    return new winston.Logger({
+    const log = new winston.Logger({
         transports: [
             new winston.transports.File({
+                name: 'info-log',
                 level: 'info',
                 filename: `${process.cwd()}/logs/all.log`,
                 handleException: true,
@@ -20,15 +23,20 @@ function logger(module) {
                 colorize: false,
             }),
             new winston.transports.Console({
+                name: 'debug-log',
                 level: 'debug',
                 label: getFilePath(module),
                 handleException: true,
-                json: true,
+                json: false,
                 colorize: true,
             }),
         ],
         exitOnError: false,
     });
+    if (!config.get('debug')) {
+        log.remove('debug-log');
+    }
+    return log;
 }
 
 module.exports = logger;
-- 
GitLab


From 8b43b5fe511fe0acbdea8bfda7df7f275cd4d958 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 11:28:08 -0300
Subject: [PATCH 53/58] Add root parameter to .eslintrc.json
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 .eslintrc.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.eslintrc.json b/.eslintrc.json
index 52b136d7..14152dfc 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,5 +1,6 @@
 {
     "extends": "airbnb",
+    "root": true,
     "plugins": [
         "react",
         "jsx-a11y",
@@ -8,6 +9,6 @@
     "rules": {
         "indent": [ "error", 4 ],
         "no-unused-vars": [ "error", { "args": "none" }],
-		"no-param-reassign": [ "off" ]
+        "no-param-reassign": [ "off" ]
     }
 }
-- 
GitLab


From 2b8c2f2c1c455a0576aa84a2c96c308576517687 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 11:28:32 -0300
Subject: [PATCH 54/58] Add .gulp-cache to .gitignore
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 .gitignore | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.gitignore b/.gitignore
index 6cb01f09..9895c657 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,5 @@ results
 npm-debug.log
 node_modules/
 build/*
+
+.gulpcache
-- 
GitLab


From fada984184b0afc70cda776a4e8d0a89ab30f750 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 11:29:07 -0300
Subject: [PATCH 55/58] Fix gulp-cache name in .gitignore
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 .gitignore | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index 9895c657..aba96774 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,4 @@ npm-debug.log
 node_modules/
 build/*
 
-.gulpcache
+.gulp-cache
-- 
GitLab


From 28c0be22e3510e52b0419f6947df06423e3cd7c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 11:29:23 -0300
Subject: [PATCH 56/58] Add dirty-chai to tests
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 src/test/test.js | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/test/test.js b/src/test/test.js
index 58e774f8..dde277d0 100644
--- a/src/test/test.js
+++ b/src/test/test.js
@@ -1,5 +1,9 @@
 const chai = require('chai');
 
+const dirtyChai = require('dirty-chai');
+
+chai.use(dirtyChai);
+
 const chaiHttp = require('chai-http');
 
 const assert = chai.assert;
-- 
GitLab


From 5c89823c0217469bb5b1b2abf9721bcb69f68548 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 11:29:42 -0300
Subject: [PATCH 57/58] Add .eslintignore to ignore test directory
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 .eslintignore | 3 +++
 1 file changed, 3 insertions(+)
 create mode 100644 .eslintignore

diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 00000000..ca947a13
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,3 @@
+[
+  "**/test/*.js"
+]
-- 
GitLab


From 501b00e6c3fd3f24fadfe8ea0e593a073723ed4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Victor=20Tozatti=20Risso?= <jvtr12@inf.ufpr.br>
Date: Mon, 5 Sep 2016 11:39:33 -0300
Subject: [PATCH 58/58] Change GitLab CI to use Gulp test task
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: João Victor Tozatti Risso <jvtr12@inf.ufpr.br>
---
 .gitlab-ci.yml | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 12a95c40..b3949c4f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,8 +8,6 @@ before_script:
 run_tests:
   stage: test
   script:
-    - gulp
-    - cd build
-    - mocha
+    - gulp test
   tags:
     - node
-- 
GitLab