From 18ac5587c658cc824745a4e30fafe5bb0bd3cd21 Mon Sep 17 00:00:00 2001
From: SimCAQ-Homologa <root@simcaqhomologa.c3sl.ufpr.br>
Date: Thu, 6 Apr 2023 09:17:34 -0300
Subject: [PATCH 001/123] Inicializa migracao

---
 src/libs/api_mongo.txt | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 src/libs/api_mongo.txt

diff --git a/src/libs/api_mongo.txt b/src/libs/api_mongo.txt
new file mode 100644
index 00000000..c1353348
--- /dev/null
+++ b/src/libs/api_mongo.txt
@@ -0,0 +1,5 @@
+*downloads.js
+*resetToken.js
+*simulation.js
+*user.js
+*verifyToken.js
-- 
GitLab


From 083b5758cd1bf26fd0b4417ef96631d56b54d75f Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 10 Apr 2023 11:44:33 -0300
Subject: [PATCH 002/123] Setting up a Sequelize

---
 package.json            |   2 +
 src/libs/models/user.js | 247 +++++++++++++++++++++++-----------------
 2 files changed, 147 insertions(+), 102 deletions(-)

diff --git a/package.json b/package.json
index 7dfdb851..672cb1d7 100644
--- a/package.json
+++ b/package.json
@@ -61,6 +61,8 @@
     "passport-http-bearer": "^1.0.1",
     "passport-oauth2-client-password": "^0.1.2",
     "request": "^2.88.0",
+    "sequelize": "^6.31.0",
+    "sequelize-cli": "^6.6.0",
     "sqlstring": "^2.3.1",
     "squel": "^5.12.2",
     "winston": "^2.4.4"
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index be62f4a5..d7c992f0 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -1,119 +1,162 @@
-const mongoose = require('mongoose');
+const Sequelize = require("sequelize");
 const crypto = require('crypto')
 const libs = `${process.cwd()}/libs`;
 const log = require(`${libs}/log`)(module);
-const Schema = mongoose.Schema;
 
-// set up a mongoose model
-var UserSchema = new Schema({
-    email: {
-        type: String,
-        unique: true,
-        required: [true, 'O campo Email é obrigatório.']
-    },
-    hashedPassword: {
-        type: String,
-        required: [true, 'O campo Senha é obrigatório.']
-    },
-    salt: {
-        type: String,
-        required: true
-    },
-    name: {
-        type: String,
-        required: [true, 'O campo Nome é obrigatório.']
-    },
-    nickname: {
-        type: String,
-        required: [true, 'O campo Apelido é obrigatório.']
-    },
-    cpf:{
-        type: String,
-        unique: true,
-        required: [true, 'O campo CPF é obrigatório.']
-    },
-    cep:{
-        type: String,
-        required: [true, 'O campo CEP é obrigatório.']
-    },
-    schooling: {
-        type: String,
-        required: [true, 'O campo Formação é obrigatório.']
-    },
-    course: {
-        type: String,
-    },
-    complement: {
-        type: String,
-    },
-    address: {
-        type: String,
-    },
-    phone: {
-        type: String,
-    },
-    segment: {
-        type: String,
-        required: [true, 'O campo Segmento é obrigatório.']
-    },
-    role: {
-        type: String,
-        required: [true, 'O campo Função é obrigatório.']
-    },
-    institutionName: {
-        type: String,
-        required: [true, 'O campo Instituição em que trabalha ou estuda é obrigatório.']
-    },
-    state: {
-        type: String,
-        required: [true, 'O campo Estado é obrigatório.']
-    },
-    city: {
-        type: String,
-        required: [true, 'O campo Cidade é obrigatório.']
-    },
-    receiveEmails: {
-        type: Boolean
-    },
-    createdAt: {
-        type: Date,
-        default: Date.now
-    },
-    origin: {
-        type: String,
-        enum: ['LDE', 'SimCAQ', 'MAPFOR'],
-        required: [true, 'O campo origem é obrigatória e aceita apenas os valores "LDE", "SimCAQ" e "MAPFOR"']
-    },
-    verified: {
-        type: Boolean,
-        default: false
-    },
-    citesegment: {
-        type: String
-    },
-    citerole: {
-        type: String
-    },
-    admin: {
-        type: Boolean,
-        default: false
-    }
-});
+// set up a sequelize model 
+var User = sequelize.define("User",{
+        email: {
+            type: Sequelize.STRING,
+            allowNull: false,
+            unique: true,
+            validate: {
+                notNull: { msg: "O campo Email é obrigatório." },
+            }
+        },
+        hashed_password:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo Senha é obrigatório." },
+            }
+        },
+        salt: {
+           type: Sequelize.STRING,
+           allowNull: false
+        },
+        name:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo Nome é obrigatório." },
+            }
+        },
+        nickname:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo Apelido é obrigatório." },
+            }
+        },
+        cpf:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            unique: true,
+            validate: {
+                notNull: { msg: "O campo CPF é obrigatório." },
+            }
+        },
+        cep:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo CEP é obrigatório." },
+            }
+        },
+        schooling:{
+            type: Sequelize.STRING,
+            allowNull: false, 
+            validate: {
+                notNull: { msg: "O campo Formação é obrigatório." },
+            }
+        },
+        course:{
+            type: Sequelize.STRING,
+            allowNull: false,
+        },
+        complement:{
+            type: Sequelize.STRING,
+            allowNull: false,
+        },
+        address:{
+            type: Sequelize.STRING,
+            allowNull: false,
+        },
+        phone:{
+            type: Sequelize.STRING,
+            allowNull: false,
+        },
+        segment:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo Segmento é obrigatório." },
+            }
+        },
+        role:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo Função é obrigatório." },
+            }
+        },
+        institution_name:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo Instituição em que trabalha ou estuda é obrigatório." },
+            }
+        },
+        state:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo Estado é obrigatório." },
+            }
+        },
+        city:{
+            type: Sequelize.STRING,
+            allowNull: false,
+            validate: {
+                notNull: { msg: "O campo Cidade é obrigatório." },
+            }
+        },
+        receive_email:{
+            type: Sequelize.BOOLEAN,
+        },
+        created_at:{
+            type: Sequelize.DATE,
+            default: Date.now
+        },
+        origin:{
+            type: Sequelize.ENUM("LDE", "SimCAQ", "MAPFOR"),
+            allowNull:false,
+            validate: {
+                notNull: { msg: "O campo origem é obrigatória e aceita apenas os valores 'LDE', 'SimCAQ' e 'MAPFOR'."},
+            }
+        },
+        verified:{
+            type: Sequelize.BOOLEAN,
+            default:false
+        },
+        citesegment:{
+            type: Sequelize.STRING
+        },
+        citerole:{
+            type: Sequelize.STRING
+        },
+        admin:{
+            type: Sequelize.BOOLEAN,
+            default:false
+        }
+    },
+      {}
+    );
 
-UserSchema.methods.encryptPassword = function(password) {
+User.methods.encryptPassword = function(password) {
     return crypto.pbkdf2Sync(password+'', this.salt, 10000, 512, 'sha512');
 };
 
-UserSchema.virtual('password').set(function(password) {
+User.virtual('password').set(function(password) {
     this._plainPassword = password+'';
     this.salt = crypto.randomBytes(128).toString('hex');
-    this.hashedPassword = this.encryptPassword(password).toString('hex');
+    this.hashed_password = this.encryptPassword(password).toString('hex');
 }).get(function() {
     return this._plainPassword;
 });
 
 UserSchema.methods.checkPassword = function(password) {
-    return this.encryptPassword(password).toString('hex') === this.hashedPassword;
+    return this.encryptPassword(password).toString('hex') === this.hashed_password;
 }
 
-module.exports = mongoose.model('User', UserSchema);
+return User; 
-- 
GitLab


From 5d788385b7723bf5ac48607e93130b1e753a451e Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Tue, 11 Apr 2023 10:28:08 -0300
Subject: [PATCH 003/123] Postgres implementation

---
 package.json            |   1 +
 src/libs/app.js         |   2 +-
 src/libs/db/mongoose.js |  21 ---
 src/libs/db/postgres.js |  21 +++
 src/libs/models/user.js | 276 ++++++++++++++++++++--------------------
 5 files changed, 161 insertions(+), 160 deletions(-)
 delete mode 100644 src/libs/db/mongoose.js
 create mode 100644 src/libs/db/postgres.js

diff --git a/package.json b/package.json
index 672cb1d7..54eb00db 100644
--- a/package.json
+++ b/package.json
@@ -60,6 +60,7 @@
     "passport": "^0.3.2",
     "passport-http-bearer": "^1.0.1",
     "passport-oauth2-client-password": "^0.1.2",
+    "pg": "^8.10.0",
     "request": "^2.88.0",
     "sequelize": "^6.31.0",
     "sequelize-cli": "^6.6.0",
diff --git a/src/libs/app.js b/src/libs/app.js
index 9f185105..9731038a 100644
--- a/src/libs/app.js
+++ b/src/libs/app.js
@@ -19,7 +19,7 @@ const api_v2 = require('./routes_v2/api');
 
 const passport = require('passport');
 
-const mongoose = require(`${libs}/db/mongoose`);
+const postgres = require(`${libs}/db/postgres`);
 
 const db = mongoose();
 
diff --git a/src/libs/db/mongoose.js b/src/libs/db/mongoose.js
deleted file mode 100644
index f9d2ed8e..00000000
--- a/src/libs/db/mongoose.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const libs = `${process.cwd()}/libs`;
-
-const config = require(`${libs}/config`);
-
-const log = require(`${libs}/log`)(module);
-
-const mongoose = require('mongoose');
-
-mongoose.Promise = global.Promise;
-
-module.exports = () => {
-    // Get mongodb URI (ip and port) in config file
-    const mongoUri = process.env.MONGO_URI || config.mongodb.uri;
-    log.info(`Connecting to MongoDB on URI ${mongoUri}`);
-    // Connection singleton
-    const db = mongoose.connect(mongoUri);
-
-    mongoose.connection.once('open', () => { log.info("MongoDB connected"); });
-
-    return db;
-};
diff --git a/src/libs/db/postgres.js b/src/libs/db/postgres.js
new file mode 100644
index 00000000..58c6d59e
--- /dev/null
+++ b/src/libs/db/postgres.js
@@ -0,0 +1,21 @@
+const libs = `${process.cwd()}/libs`;
+
+const config = require(`${libs}/config`);
+
+const log = require(`${libs}/log`)(module);
+
+const { Pool} = require('pg');
+
+module.exports = () => {
+    
+    const pool = new Pool({
+        user: "postgres",
+        database: "postgres",
+        password: "postgres",
+        port: 5432,
+        host: "localhost",
+
+    });
+    
+    return pool;
+};
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index d7c992f0..4d40c4e2 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -5,145 +5,145 @@ const log = require(`${libs}/log`)(module);
 
 // set up a sequelize model 
 var User = sequelize.define("User",{
-        email: {
-            type: Sequelize.STRING,
-            allowNull: false,
-            unique: true,
-            validate: {
-                notNull: { msg: "O campo Email é obrigatório." },
-            }
-        },
-        hashed_password:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo Senha é obrigatório." },
-            }
-        },
-        salt: {
-           type: Sequelize.STRING,
-           allowNull: false
-        },
-        name:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo Nome é obrigatório." },
-            }
-        },
-        nickname:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo Apelido é obrigatório." },
-            }
-        },
-        cpf:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            unique: true,
-            validate: {
-                notNull: { msg: "O campo CPF é obrigatório." },
-            }
-        },
-        cep:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo CEP é obrigatório." },
-            }
-        },
-        schooling:{
-            type: Sequelize.STRING,
-            allowNull: false, 
-            validate: {
-                notNull: { msg: "O campo Formação é obrigatório." },
-            }
-        },
-        course:{
-            type: Sequelize.STRING,
-            allowNull: false,
-        },
-        complement:{
-            type: Sequelize.STRING,
-            allowNull: false,
-        },
-        address:{
-            type: Sequelize.STRING,
-            allowNull: false,
-        },
-        phone:{
-            type: Sequelize.STRING,
-            allowNull: false,
-        },
-        segment:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo Segmento é obrigatório." },
-            }
-        },
-        role:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo Função é obrigatório." },
-            }
-        },
-        institution_name:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo Instituição em que trabalha ou estuda é obrigatório." },
-            }
-        },
-        state:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo Estado é obrigatório." },
-            }
-        },
-        city:{
-            type: Sequelize.STRING,
-            allowNull: false,
-            validate: {
-                notNull: { msg: "O campo Cidade é obrigatório." },
-            }
-        },
-        receive_email:{
-            type: Sequelize.BOOLEAN,
-        },
-        created_at:{
-            type: Sequelize.DATE,
-            default: Date.now
-        },
-        origin:{
-            type: Sequelize.ENUM("LDE", "SimCAQ", "MAPFOR"),
-            allowNull:false,
-            validate: {
-                notNull: { msg: "O campo origem é obrigatória e aceita apenas os valores 'LDE', 'SimCAQ' e 'MAPFOR'."},
-            }
-        },
-        verified:{
-            type: Sequelize.BOOLEAN,
-            default:false
-        },
-        citesegment:{
-            type: Sequelize.STRING
-        },
-        citerole:{
-            type: Sequelize.STRING
-        },
-        admin:{
-            type: Sequelize.BOOLEAN,
-            default:false
+    email: {
+        type: Sequelize.STRING,
+        allowNull: false,
+        unique: true,
+        validate: {
+            notNull: { msg: "O campo Email é obrigatório." },
         }
     },
-      {}
-    );
+    hashed_password:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo Senha é obrigatório." },
+        }
+    },
+    salt: {
+        type: Sequelize.STRING,
+        allowNull: false
+    },
+    name:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo Nome é obrigatório." },
+        }
+    },
+    nickname:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo Apelido é obrigatório." },
+        }
+    },
+    cpf:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        unique: true,
+        validate: {
+            notNull: { msg: "O campo CPF é obrigatório." },
+        }
+    },
+    cep:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo CEP é obrigatório." },
+        }
+    },
+    schooling:{
+        type: Sequelize.STRING,
+        allowNull: false, 
+        validate: {
+            notNull: { msg: "O campo Formação é obrigatório." },
+        }
+    },
+    course:{
+        type: Sequelize.STRING,
+        allowNull: false,
+    },
+    complement:{
+        type: Sequelize.STRING,
+        allowNull: false,
+    },
+    address:{
+        type: Sequelize.STRING,
+        allowNull: false,
+    },
+    phone:{
+        type: Sequelize.STRING,
+        allowNull: false,
+    },
+    segment:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo Segmento é obrigatório." },
+        }
+    },
+    role:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo Função é obrigatório." },
+        }
+    },
+    institution_name:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo Instituição em que trabalha ou estuda é obrigatório." },
+        }
+    },
+    state:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo Estado é obrigatório." },
+        }
+    },
+    city:{
+        type: Sequelize.STRING,
+        allowNull: false,
+        validate: {
+            notNull: { msg: "O campo Cidade é obrigatório." },
+        }
+    },
+    receive_email:{
+        type: Sequelize.BOOLEAN,
+    },
+    created_at:{
+        type: Sequelize.DATE,
+        default: Date.now
+    },
+    origin:{
+        type: Sequelize.ENUM("LDE", "SimCAQ", "MAPFOR"),
+        allowNull:false,
+        validate: {
+            notNull: { msg: "O campo origem é obrigatória e aceita apenas os valores 'LDE', 'SimCAQ' e 'MAPFOR'."},
+        }
+    },
+    verified:{
+        type: Sequelize.BOOLEAN,
+        default:false
+    },
+    citesegment:{
+        type: Sequelize.STRING
+    },
+    citerole:{
+        type: Sequelize.STRING
+    },
+    admin:{
+        type: Sequelize.BOOLEAN,
+        default:false
+    }
+},
+    {}
+);
 
-User.methods.encryptPassword = function(password) {
+User.encryptPassword = function(password) {
     return crypto.pbkdf2Sync(password+'', this.salt, 10000, 512, 'sha512');
 };
 
@@ -155,8 +155,8 @@ User.virtual('password').set(function(password) {
     return this._plainPassword;
 });
 
-UserSchema.methods.checkPassword = function(password) {
+User.checkPassword = function(password) {
     return this.encryptPassword(password).toString('hex') === this.hashed_password;
 }
 
-return User; 
+module.exports = User; 
-- 
GitLab


From 6ab0f7ba930888b630edec3c1ea6ec3f59bc6036 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 13 Apr 2023 10:40:44 -0300
Subject: [PATCH 004/123] Make VerifyToken and adapta FindById to FindOne

---
 config.json.example                  | 157 ---------------------------
 script_req.sh                        |  10 ++
 src/libs/app.js                      |   4 -
 src/libs/db/postgres.js              |  23 +---
 src/libs/middlewares/oauth2.js       |   3 +-
 src/libs/middlewares/passport.js     |   2 +-
 src/libs/models/user.js              |  59 ++++++++--
 src/libs/models/verificationToken.js |  39 +++----
 src/libs/routes_v1/resetToken.js     |   4 +-
 src/libs/routes_v1/user.js           |   6 +-
 src/libs/routes_v1/verifyToken.js    |   2 +-
 11 files changed, 91 insertions(+), 218 deletions(-)
 delete mode 100644 config.json.example
 create mode 100755 script_req.sh

diff --git a/config.json.example b/config.json.example
deleted file mode 100644
index 1fa157c1..00000000
--- a/config.json.example
+++ /dev/null
@@ -1,157 +0,0 @@
-{
-    "development":
-    {
-        "port": 3000,
-        "ip": "127.0.0.1",
-        "debug" : true,
-        "monetdb": {
-            "host": "simcaqdb3.c3sl.ufpr.br",
-            "port": 50000,
-            "dbname": "simcaq",
-            "user": "monetdb",
-            "password":"monetdb",
-            "nrConnections": "4"
-        },
-        "cdn" : {
-            "url": "http://simcaqdb3.c3sl.ufpr.br:3000",
-            "download": "https://simcaqdev.c3sl.ufpr.br/download/"
-        },
-        "mongodb" : {
-            "uri": "mongodb://localhost/dev_users"
-        },
-        "monq": {
-            "uri": "mongodb://localhost/dev_monq"
-        },
-        "default": {
-            "api": {
-                "version" : "v1"
-            },
-            "lde": {
-                "url": "http://ldedev.c3sl.ufpr.br/#"
-            },
-            "simcaq": {
-                "url": "http://simcaqdev.c3sl.ufpr.br/#"
-            }
-        },
-        "email": {
-            "host": "SMTP.office365.com",
-            "port": 587,
-            "secureConnection": false,
-            "auth": {
-                "user": "dadoseducacionais@ufpr.br",
-                "pass": "COLOCAR_A_SENHA_AQUI"
-            },
-            "tls": {
-                "ciphers": "SSLv3"
-            },
-            "from": "\"Laboratório de Dados Educacionais\" <dadoseducacionais@ufpr.br>"
-        },
-        "security": {
-            "tokenLife": 3600
-        }
-    },
-    "test":
-    {
-        "port": 3000,
-        "ip": "127.0.0.1",
-        "debug" : true,
-        "monetdb": {
-            "host": "simcaqdb3.c3sl.ufpr.br",
-            "port": 50000,
-            "dbname": "simcaq",
-            "user": "monetdb",
-            "password":"monetdb",
-            "nrConnections": "4"
-        },
-        "cdn" : {
-            "url": "http://simcaqdb3.c3sl.ufpr.br:3000",
-            "download": "https://simcaqdev.c3sl.ufpr.br/download/"
-        },
-        "mongodb" : {
-            "uri": "mongodb://localhost/test_users",
-            "secret": "SimCAQC3SL"
-        },
-        "monq": {
-            "uri": "mongodb://localhost/test_monq"
-        },
-        "default": {
-            "api": {
-                "version" : "v1"
-            },
-            "lde": {
-                "url": "http://ldedev.c3sl.ufpr.br/#"
-            },
-            "simcaq": {
-                "url": "http://simcaqdev.c3sl.ufpr.br/#"
-            }
-        },
-        "email": {
-            "host": "SMTP.office365.com",
-            "port": 587,
-            "secureConnection": false,
-            "auth": {
-                "user": "dadoseducacionais@ufpr.br",
-                "pass": "COLOCAR_A_SENHA_AQUI"
-            },
-            "tls": {
-                "ciphers": "SSLv3"
-            },
-            "from": "\"Laboratório de Dados Educacionais\" <dadoseducacionais@ufpr.br>"
-        },
-        "security": {
-            "tokenLife": 3600
-        }
-    },
-    "production":
-    {
-        "port": 3000,
-        "ip": "127.0.0.1",
-        "debug" : false,
-        "monetdb": {
-            "host": "simcaqdb3.c3sl.ufpr.br",
-            "port": 50000,
-            "dbname": "simcaq",
-            "user": "monetdb",
-            "password":"monetdb",
-            "nrConnections": "4"
-        },
-        "cdn" : {
-            "url": "http://simcaqdb3.c3sl.ufpr.br:7000",
-            "download": "https://simcaq.c3sl.ufpr.br/download/"
-        },
-        "mongodb" : {
-            "uri": "mongodb://localhost/users",
-            "secret": "SimCAQC3SL"
-        },
-        "monq": {
-            "uri": "mongodb://localhost/monq"
-        },
-        "default": {
-            "api": {
-                "version" : "v1"
-            },
-            "lde": {
-                "url": "http://lde.c3sl.ufpr.br/#"
-            },
-            "simcaq": {
-                "url": "http://simcaq.c3sl.ufpr.br/#"
-            }
-        },
-        "email": {
-            "host": "SMTP.office365.com",
-            "port": 587,
-            "secureConnection": false,
-            "auth": {
-                "user": "dadoseducacionais@ufpr.br",
-                "pass": "COLOCAR_A_SENHA_AQUI"
-            },
-            "tls": {
-                "ciphers": "SSLv3"
-            },
-            "from": "\"Laboratório de Dados Educacionais\" <dadoseducacionais@ufpr.br>"
-        },
-        "security": {
-            "tokenLife": 3600
-        }
-    }
-}
diff --git a/script_req.sh b/script_req.sh
new file mode 100755
index 00000000..125204b8
--- /dev/null
+++ b/script_req.sh
@@ -0,0 +1,10 @@
+curl -X 'POST' \
+  'http://10.254.221.20:3000/api/v1//auth/token' \
+  -H 'accept: application/json' \
+  -H 'Content-Type: application/json' \
+  -d '{
+  "email": "eduardomsouza@ufpr.br",
+  "password": "teste123",
+  "client_secret": "LDE",
+  "grant_type": "password"
+}'
\ No newline at end of file
diff --git a/src/libs/app.js b/src/libs/app.js
index 9731038a..bab5e52d 100644
--- a/src/libs/app.js
+++ b/src/libs/app.js
@@ -19,10 +19,6 @@ const api_v2 = require('./routes_v2/api');
 
 const passport = require('passport');
 
-const postgres = require(`${libs}/db/postgres`);
-
-const db = mongoose();
-
 require(`${libs}/middlewares/passport`);
 
 app.use(bodyParser.json({limit: '50mb'}));
diff --git a/src/libs/db/postgres.js b/src/libs/db/postgres.js
index 58c6d59e..c60d28c6 100644
--- a/src/libs/db/postgres.js
+++ b/src/libs/db/postgres.js
@@ -1,21 +1,8 @@
-const libs = `${process.cwd()}/libs`;
+const Sequelize = require('sequelize');
 
-const config = require(`${libs}/config`);
+// if you are using postgres, your DB URL will look like this
+const DATABASE_URL = 'postgres://postgres:postgres@localhost:5432/postgres'
 
-const log = require(`${libs}/log`)(module);
+const db = new Sequelize(DATABASE_URL)
 
-const { Pool} = require('pg');
-
-module.exports = () => {
-    
-    const pool = new Pool({
-        user: "postgres",
-        database: "postgres",
-        password: "postgres",
-        port: 5432,
-        host: "localhost",
-
-    });
-    
-    return pool;
-};
+module.exports = db
\ No newline at end of file
diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index cc9a45ed..4cb6adcd 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -7,7 +7,6 @@ const libs = `${process.cwd()}/libs`;
 const config = require(`${libs}/config`);
 const log = require(`${libs}/log`)(module);
 
-const db = require(`${libs}/db/mongoose`);
 const User = require(`${libs}/models/user`);
 const AccessToken = require(`${libs}/models/accessToken`);
 const RefreshToken = require(`${libs}/models/refreshToken`);
@@ -88,7 +87,7 @@ aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope,
             return done(null, false);
         }
 
-        User.findById(token.userId, (err, user)  => {
+        User.findOne({token: token.userId} , (err, user)  => {
             if (err) { 
                 log.error(err);
                 return done(err);
diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js
index ab895a96..246a95ea 100644
--- a/src/libs/middlewares/passport.js
+++ b/src/libs/middlewares/passport.js
@@ -50,7 +50,7 @@ passport.use(new BearerStrategy( (accessToken, done) => {
                 return done(null, false, { msg: 'Token expired' });
             }
 
-            User.findById(token.userId, function(err, usuario) {
+            User.findOne({token: token.userId}, function(err, usuario) {
                 if (err) {
                     return done(err);
                 }
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 4d40c4e2..04fcb892 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -1,10 +1,16 @@
 const Sequelize = require("sequelize");
 const crypto = require('crypto')
+const db = require('../db/postgres.js')
 const libs = `${process.cwd()}/libs`;
 const log = require(`${libs}/log`)(module);
 
 // set up a sequelize model 
-var User = sequelize.define("User",{
+var User = db.define("User",{
+    id:{
+        type: Sequelize.STRING,
+        allowNull:false,
+        unique: true
+    },
     email: {
         type: Sequelize.STRING,
         allowNull: false,
@@ -13,16 +19,28 @@ var User = sequelize.define("User",{
             notNull: { msg: "O campo Email é obrigatório." },
         }
     },
+    password:{
+        type: Sequelize.STRING,
+        get(){
+            return () => this.getDataValue('password')
+        }
+    },
     hashed_password:{
         type: Sequelize.STRING,
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Senha é obrigatório." },
+        },
+        get() {
+            return() => this.getDataValue('salt')
         }
     },
     salt: {
         type: Sequelize.STRING,
-        allowNull: false
+        allowNull: false,
+        get() {
+            return() => this.getDataValue('salt')
+        }
     },
     name:{
         type: Sequelize.STRING,
@@ -143,20 +161,39 @@ var User = sequelize.define("User",{
     {}
 );
 
+User.generateSalt = function() {
+    return crypto.randomBytes(128).toString('hex');
+}
+
 User.encryptPassword = function(password) {
     return crypto.pbkdf2Sync(password+'', this.salt, 10000, 512, 'sha512');
+}
+
+User.generateObjectId = function(){
+    var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
+    return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function() {
+        return (Math.random() * 16 | 0).toString(16);
+    }).toLowerCase();
+}
+
+const setSaltAndPassword = user => {
+    if (user.changed('password')) {
+        user.salt = User.generateSalt()
+        user.password = User.encryptPassword(user.password())
+        this.hashed_password = this.encryptPassword(password).toString('hex');
+    }
+}
+
+const setObjectId = user => {
+   user.id = User.generateObjectId()
 };
 
-User.virtual('password').set(function(password) {
-    this._plainPassword = password+'';
-    this.salt = crypto.randomBytes(128).toString('hex');
-    this.hashed_password = this.encryptPassword(password).toString('hex');
-}).get(function() {
-    return this._plainPassword;
-});
+User.beforeCreate(setSaltAndPassword)
+User.beforeCreate(setObjectId)
+User.beforeUpdate(setSaltAndPassword)
 
-User.checkPassword = function(password) {
-    return this.encryptPassword(password).toString('hex') === this.hashed_password;
+User.prototype.checkPassword = function(enteredPassword) {
+    return User.encryptPassword(enteredPassword, this.salt()) === this.hashed_password()
 }
 
 module.exports = User; 
diff --git a/src/libs/models/verificationToken.js b/src/libs/models/verificationToken.js
index ef7e109e..793ed56c 100644
--- a/src/libs/models/verificationToken.js
+++ b/src/libs/models/verificationToken.js
@@ -1,33 +1,32 @@
-const mongoose = require('mongoose');
-const Schema = mongoose.Schema;
-const libs = `${process.cwd()}/libs`;
+const Sequelize = require("sequelize");
+const crypto = require('crypto')
+const db = require('../db/postgres.js')
 const log = require(`${libs}/log`)(module);
 const User = require(`${libs}/models/user`);
 const uuid = require('node-uuid');
 
-let VerificationToken = new Schema({
+var VerificationToken = db.define("VerificationToken",{
     userId: {
-        type: Schema.Types.ObjectId,
-        required: true,
-        ref: 'User'
+        type: Sequelize.STRING, 
+        allowNull: false
     },
-    token: {
-        type: String,
-        required: true
+    token:{
+        type: Sequelize.STRING,
+        allowNull: false
     },
-    verified: {
-        type: Boolean,
-        required: true,
+    verified:{
+        type: Sequelize.BOOLEAN,
+        allowNull: false,
         default: false
     },
-    createdAt: {
-        type: Date,
-        required: true,
+    createdAt:{
+        type: Sequelize.DATE,
+        allowNull: false,
         default: Date.now
     }
-});
+})
 
-VerificationToken.methods.createVerificationToken = function(done) {
+VerificationToken.createVerificationToken = function(done) {
     let verificationToken = this;
     let token = uuid.v4();
     verificationToken.set('token', token);
@@ -38,4 +37,6 @@ VerificationToken.methods.createVerificationToken = function(done) {
     })
 }
 
-module.exports = mongoose.model('VerificationToken', VerificationToken);
+VerificationToken.belongsTo(User);
+
+module.exports = VerificationToken;
diff --git a/src/libs/routes_v1/resetToken.js b/src/libs/routes_v1/resetToken.js
index 34ece845..faa4caa6 100644
--- a/src/libs/routes_v1/resetToken.js
+++ b/src/libs/routes_v1/resetToken.js
@@ -32,7 +32,7 @@ resetTokenApp.get('/:token', (req, res, next) => {
             })
             return next({msg: 'Token expired', status: 410});
         }
-        User.findById(rToken.userId, (err, user) => {
+        User.findOne({token: rToken.userId}, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
@@ -55,7 +55,7 @@ resetTokenApp.post('/:token', (req, res, next) => {
             res.statusCode = 404;
             return next({msg: 'Token not found', status:404});
         }
-        User.findById(rToken.userId, (err, user) => {
+        User.findOne({token: rToken.userId}, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 7ff088ee..951717d7 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -100,7 +100,7 @@ userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, re
 }, response('user'));
 
 userApp.get('/:id', (req, res, next) => {
-  User.findById(req.params.id, (err, user) => {
+  User.findOne({id: req.params.id}, (err, user) => {
     if(err) {
       log.error(err);
       return next(err);
@@ -161,7 +161,7 @@ userApp.post('/', (req, res, next) => {
 
       // Create verification token
       let verificationToken = new VerificationToken({
-        userId: user._id
+        userId: user.id
       });
 
       verificationToken.createVerificationToken((err, token) => {
@@ -195,7 +195,7 @@ userApp.post('/', (req, res, next) => {
 });
 
 userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
-  User.findById(req.params.id, (err, user) => {
+  User.findOne({id:req.params.id}, (err, user) => {
     if (err) {
       log.error(err);
       return next({err});
diff --git a/src/libs/routes_v1/verifyToken.js b/src/libs/routes_v1/verifyToken.js
index d54f64aa..ecb834b3 100644
--- a/src/libs/routes_v1/verifyToken.js
+++ b/src/libs/routes_v1/verifyToken.js
@@ -22,7 +22,7 @@ verifyTokenApp.get('/:token', (req, res, next) => {
             res.statusCode = 404;
             return next({msg: 'Token not found', status:404});
         }
-        User.findById(vToken.userId, (err, user) => {
+        User.findOne({token:vToken.userId}, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
-- 
GitLab


From 9371dd1b4208be7b5ebfc74765abae9d2d90ef39 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 13 Apr 2023 11:55:08 -0300
Subject: [PATCH 005/123] Adjusting models sequelize

---
 src/libs/middlewares/oauth2.js       | 2 +-
 src/libs/middlewares/passport.js     | 2 +-
 src/libs/models/user.js              | 6 +++---
 src/libs/models/verificationToken.js | 6 ++----
 src/libs/routes_v1/resetToken.js     | 4 ++--
 src/libs/routes_v1/user.js           | 4 ++--
 src/libs/routes_v1/verifyToken.js    | 2 +-
 src/libs/routes_v2/resetToken.js     | 4 ++--
 src/libs/routes_v2/user.js           | 4 ++--
 src/libs/routes_v2/verifyToken.js    | 2 +-
 10 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 4cb6adcd..1021c58d 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -87,7 +87,7 @@ aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope,
             return done(null, false);
         }
 
-        User.findOne({token: token.userId} , (err, user)  => {
+        User.findByPk(token.userId , (err, user)  => {
             if (err) { 
                 log.error(err);
                 return done(err);
diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js
index 246a95ea..393e5c46 100644
--- a/src/libs/middlewares/passport.js
+++ b/src/libs/middlewares/passport.js
@@ -50,7 +50,7 @@ passport.use(new BearerStrategy( (accessToken, done) => {
                 return done(null, false, { msg: 'Token expired' });
             }
 
-            User.findOne({token: token.userId}, function(err, usuario) {
+            User.findByPk(token.userId, function(err, usuario) {
                 if (err) {
                     return done(err);
                 }
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 04fcb892..c659b6f2 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -2,14 +2,14 @@ const Sequelize = require("sequelize");
 const crypto = require('crypto')
 const db = require('../db/postgres.js')
 const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
 
 // set up a sequelize model 
 var User = db.define("User",{
     id:{
         type: Sequelize.STRING,
         allowNull:false,
-        unique: true
+        unique: true,
+        primaryKey: true
     },
     email: {
         type: Sequelize.STRING,
@@ -20,7 +20,7 @@ var User = db.define("User",{
         }
     },
     password:{
-        type: Sequelize.STRING,
+        type: Sequelize.VIRTUAL,
         get(){
             return () => this.getDataValue('password')
         }
diff --git a/src/libs/models/verificationToken.js b/src/libs/models/verificationToken.js
index 793ed56c..63b2b249 100644
--- a/src/libs/models/verificationToken.js
+++ b/src/libs/models/verificationToken.js
@@ -1,8 +1,6 @@
 const Sequelize = require("sequelize");
-const crypto = require('crypto')
 const db = require('../db/postgres.js')
-const log = require(`${libs}/log`)(module);
-const User = require(`${libs}/models/user`);
+const User = require(`./user.js`);
 const uuid = require('node-uuid');
 
 var VerificationToken = db.define("VerificationToken",{
@@ -19,7 +17,7 @@ var VerificationToken = db.define("VerificationToken",{
         allowNull: false,
         default: false
     },
-    createdAt:{
+    created_at:{
         type: Sequelize.DATE,
         allowNull: false,
         default: Date.now
diff --git a/src/libs/routes_v1/resetToken.js b/src/libs/routes_v1/resetToken.js
index faa4caa6..3d67a1e2 100644
--- a/src/libs/routes_v1/resetToken.js
+++ b/src/libs/routes_v1/resetToken.js
@@ -32,7 +32,7 @@ resetTokenApp.get('/:token', (req, res, next) => {
             })
             return next({msg: 'Token expired', status: 410});
         }
-        User.findOne({token: rToken.userId}, (err, user) => {
+        User.findByPk(rToken.userId, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
@@ -55,7 +55,7 @@ resetTokenApp.post('/:token', (req, res, next) => {
             res.statusCode = 404;
             return next({msg: 'Token not found', status:404});
         }
-        User.findOne({token: rToken.userId}, (err, user) => {
+        User.findByPk(rToken.userId, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 951717d7..ee5793db 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -100,7 +100,7 @@ userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, re
 }, response('user'));
 
 userApp.get('/:id', (req, res, next) => {
-  User.findOne({id: req.params.id}, (err, user) => {
+  User.findByPk(req.params.id, (err, user) => {
     if(err) {
       log.error(err);
       return next(err);
@@ -195,7 +195,7 @@ userApp.post('/', (req, res, next) => {
 });
 
 userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
-  User.findOne({id:req.params.id}, (err, user) => {
+  User.findByPk(req.params.id, (err, user) => {
     if (err) {
       log.error(err);
       return next({err});
diff --git a/src/libs/routes_v1/verifyToken.js b/src/libs/routes_v1/verifyToken.js
index ecb834b3..8cc9e35d 100644
--- a/src/libs/routes_v1/verifyToken.js
+++ b/src/libs/routes_v1/verifyToken.js
@@ -22,7 +22,7 @@ verifyTokenApp.get('/:token', (req, res, next) => {
             res.statusCode = 404;
             return next({msg: 'Token not found', status:404});
         }
-        User.findOne({token:vToken.userId}, (err, user) => {
+        User.findByPk(vToken.userId, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
diff --git a/src/libs/routes_v2/resetToken.js b/src/libs/routes_v2/resetToken.js
index 34ece845..3d67a1e2 100644
--- a/src/libs/routes_v2/resetToken.js
+++ b/src/libs/routes_v2/resetToken.js
@@ -32,7 +32,7 @@ resetTokenApp.get('/:token', (req, res, next) => {
             })
             return next({msg: 'Token expired', status: 410});
         }
-        User.findById(rToken.userId, (err, user) => {
+        User.findByPk(rToken.userId, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
@@ -55,7 +55,7 @@ resetTokenApp.post('/:token', (req, res, next) => {
             res.statusCode = 404;
             return next({msg: 'Token not found', status:404});
         }
-        User.findById(rToken.userId, (err, user) => {
+        User.findByPk(rToken.userId, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
diff --git a/src/libs/routes_v2/user.js b/src/libs/routes_v2/user.js
index 7ff088ee..a2ab63a5 100644
--- a/src/libs/routes_v2/user.js
+++ b/src/libs/routes_v2/user.js
@@ -100,7 +100,7 @@ userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, re
 }, response('user'));
 
 userApp.get('/:id', (req, res, next) => {
-  User.findById(req.params.id, (err, user) => {
+  User.findOne({id:req.params.id}, (err, user) => {
     if(err) {
       log.error(err);
       return next(err);
@@ -195,7 +195,7 @@ userApp.post('/', (req, res, next) => {
 });
 
 userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
-  User.findById(req.params.id, (err, user) => {
+  User.findOne({id:req.params.id}, (err, user) => {
     if (err) {
       log.error(err);
       return next({err});
diff --git a/src/libs/routes_v2/verifyToken.js b/src/libs/routes_v2/verifyToken.js
index d54f64aa..8978cd4b 100644
--- a/src/libs/routes_v2/verifyToken.js
+++ b/src/libs/routes_v2/verifyToken.js
@@ -22,7 +22,7 @@ verifyTokenApp.get('/:token', (req, res, next) => {
             res.statusCode = 404;
             return next({msg: 'Token not found', status:404});
         }
-        User.findById(vToken.userId, (err, user) => {
+        User.findOne({id:vToken.userId}, (err, user) => {
             if(err) {
                 log.error(err);
                 next(err);
-- 
GitLab


From fdceb2718fd88cd926f74a011f15581a795688c4 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Fri, 14 Apr 2023 09:52:30 -0300
Subject: [PATCH 006/123] Remove updateAt and createdAt

---
 src/libs/models/user.js              | 4 ++--
 src/libs/models/verificationToken.js | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index c659b6f2..1d01bcae 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -156,9 +156,9 @@ var User = db.define("User",{
     admin:{
         type: Sequelize.BOOLEAN,
         default:false
-    }
+    },
 },
-    {}
+    {timestamps: false}
 );
 
 User.generateSalt = function() {
diff --git a/src/libs/models/verificationToken.js b/src/libs/models/verificationToken.js
index 63b2b249..f6c57a05 100644
--- a/src/libs/models/verificationToken.js
+++ b/src/libs/models/verificationToken.js
@@ -22,7 +22,8 @@ var VerificationToken = db.define("VerificationToken",{
         allowNull: false,
         default: Date.now
     }
-})
+},
+{timestamps: false});
 
 VerificationToken.createVerificationToken = function(done) {
     let verificationToken = this;
-- 
GitLab


From d79515a410fd99c64ffb9a13384351f3c3ea9c9f Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Fri, 14 Apr 2023 11:18:23 -0300
Subject: [PATCH 007/123] FIX res.json

---
 src/libs/routes_v1/user.js | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index ee5793db..30691e14 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -113,9 +113,8 @@ userApp.get('/:id', (req, res, next) => {
       delete u.hashedPassword;
       delete u.salt;
       req.result = u;
-      next();
     }
-  });
+  }).then((result) => res.json(result));
 }, response('user'));
 
 userApp.post('/', (req, res, next) => {
-- 
GitLab


From 0107b08d0f5555491aca41d50080b71ba3229514 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Fri, 14 Apr 2023 11:40:39 -0300
Subject: [PATCH 008/123] Fix get

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

diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 30691e14..085b697d 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -115,7 +115,7 @@ userApp.get('/:id', (req, res, next) => {
       req.result = u;
     }
   }).then((result) => res.json(result));
-}, response('user'));
+});
 
 userApp.post('/', (req, res, next) => {
   let user = new User({
-- 
GitLab


From e758ce8f3ff3bafd815f5021f94f8ca9a7d189ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 18 Apr 2023 10:04:09 -0300
Subject: [PATCH 009/123] Add enrollment projection route

---
 src/libs/routes_v2/api.js                     |   3 +
 .../routes_v2/simcaqEnrollmentProjection.js   | 143 ++++++++++++++++++
 2 files changed, 146 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqEnrollmentProjection.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index f63dd586..25c829f9 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -154,6 +154,8 @@ const simcaqResult = require(`${libs}/routes_v2/simcaqResult`);
 
 const simcaqNumberOfTeachers = require(`${libs}/routes_v2/simcaqNumberOfTeachers`);
 
+const simcaqEnrollmentProjection = require(`${libs}/routes_v2/simcaqEnrollmentProjection`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -222,5 +224,6 @@ api.use('/simcaq_number_of_employees', simcaqNumberOfEmployees);
 api.use('/simcaq_new_classes', simcaqNewClasses);
 api.use('/simcaq_result', simcaqResult);
 api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
+api.use('/simcaq_enrollment_projection', simcaqEnrollmentProjection);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqEnrollmentProjection.js b/src/libs/routes_v2/simcaqEnrollmentProjection.js
new file mode 100644
index 00000000..cb472b4e
--- /dev/null
+++ b/src/libs/routes_v2/simcaqEnrollmentProjection.js
@@ -0,0 +1,143 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqEnrollmentProjectionApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqEnrollmentProjectionApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_projecao_de_matricula',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_projecao_de_matricula'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_projecao_de_matricula'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_projecao_de_matricula'
+    }
+}, 'dims').addValue({
+    name: 'education_level_short_id',
+    table: 'simcaq_projecao_de_matricula',
+    tableField: 'etapa',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa'
+    }
+}).addValue({
+    name: 'adm_dependency_public_id',
+    table: 'simcaq_projecao_de_matricula',
+    tableField: 'rede',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'rede'
+    }
+});
+
+simcaqEnrollmentProjectionApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_projecao_de_matricula')
+        .field('simcaq_projecao_de_matricula.ano_censo', 'year')
+        .field('simcaq_projecao_de_matricula.etapa', 'education_level_short_id')
+        .field('simcaq_projecao_de_matricula.rede', 'adm_dependency_public_id')
+        .field('SUM(simcaq_projecao_de_matricula.total_matriculas)', 'total_enrollments')
+        .field('SUM(simcaq_projecao_de_matricula.matriculas_diurno)', 'daytime_enrollments')
+        .field('SUM(simcaq_projecao_de_matricula.matriculas_noturno)', 'nighttime_enrollments')
+        .field('SUM(simcaq_projecao_de_matricula.total_tempo_integral)', 'fulltime_enrollments')
+        .group('simcaq_projecao_de_matricula.ano_censo')
+        .group('simcaq_projecao_de_matricula.etapa')
+        .group('simcaq_projecao_de_matricula.rede');
+    next();
+}, query, id2str.transform(), response('enrollmentProjection'));
+
+module.exports = simcaqEnrollmentProjectionApp;
-- 
GitLab


From c28d191ff75b9598119c5c4e74c2c9a228ca455a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 24 Apr 2023 10:02:33 -0300
Subject: [PATCH 010/123] update v2 classroom route to use new aggregated table

---
 src/libs/routes_v2/classroom.js | 54 ++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/libs/routes_v2/classroom.js b/src/libs/routes_v2/classroom.js
index 8e2b6a7e..a3d6ec7f 100644
--- a/src/libs/routes_v2/classroom.js
+++ b/src/libs/routes_v2/classroom.js
@@ -50,22 +50,22 @@ let rqfCount = new ReqQueryFields();
 // Complete range of the enrollments dataset.
 // Returns a tuple of start and ending years of the complete enrollments dataset.
 classroomApp.get('/year_range', (req, res, next) => {
-    req.sql.from('escola')
-    .field('MIN(escola.ano_censo)', 'start_year')
-    .field('MAX(escola.ano_censo)', 'end_year');
+    req.sql.from('escola_agregada')
+    .field('MIN(escola_agregada.ano_censo)', 'start_year')
+    .field('MAX(escola_agregada.ano_censo)', 'end_year');
     next();
 }, query, response('range'));
 
 classroomApp.get('/years', (req, res, next) => {
-    req.sql.from('escola')
-    .field('DISTINCT escola.ano_censo', 'year');
+    req.sql.from('escola_agregada')
+    .field('DISTINCT escola_agregada.ano_censo', 'year');
     next();
 }, query, response('years'));
 
 classroomApp.get('/source', (req, res, next) => {
     req.sql.from('fonte')
     .field('fonte', 'source')
-    .where('tabela = \'escola\'');
+    .where('tabela = \'escola_agregada\'');
     next();
 }, query, response('source'));
 
@@ -109,7 +109,7 @@ rqf.addField({
     where: false
 }).addValueToField({
     name: 'school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: ['nome_escola', 'id'],
     resultField: ['school_name', 'school_id'],
     where: {
@@ -119,7 +119,7 @@ rqf.addField({
     }
 }, 'dims').addValueToField({
     name: 'school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'nome_escola',
     resultField: 'school_name',
     where: {
@@ -136,12 +136,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'dims').addValueToField({
     name: 'city',
@@ -152,12 +152,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'filter').addValue({
     name: 'state',
@@ -168,12 +168,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'estado_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'estado_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'region',
@@ -188,11 +188,11 @@ rqf.addField({
     join: {
         primary: 'id',
         foreign: 'regiao_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'min_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -202,7 +202,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'max_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -212,7 +212,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'adm_dependency',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_id',
     resultField: 'adm_dependency_id',
     where: {
@@ -222,7 +222,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'adm_dependency_detailed',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_priv',
     resultField: 'adm_dependency_detailed_id',
     where: {
@@ -232,7 +232,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localizacao_id',
     resultField: 'location_id',
     where: {
@@ -243,14 +243,14 @@ rqf.addField({
 });
 
 classroomApp.get('/', cache('15 day'), rqf.parse(), rqf.build(), (req, res, next) => {
-    req.sql.from('escola')
-        .field('SUM(escola.qtde_salas_utilizadas_dentro)', 'total')
+    req.sql.from('escola_agregada')
+        .field('SUM(escola_agregada.qtde_salas_utilizadas_dentro)', 'total')
         .field("'Brasil'", 'name')
-        .field('escola.ano_censo', 'year')
-        .group('escola.ano_censo')
-        .order('escola.ano_censo')
-        .where('escola.situacao_de_funcionamento = 1 AND escola.local_func_predio_escolar = 1')
-        .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1');
+        .field('escola_agregada.ano_censo', 'year')
+        .group('escola_agregada.ano_censo')
+        .order('escola_agregada.ano_censo')
+        .where('escola_agregada.situacao_funcionamento_pareada = 1 AND escola_agregada.local_func_predio_escolar = 1')
+        .where('escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1');
     next();
 }, query, addMissing(rqf), id2str.transform(), (req, res, next) => {
     if (req.dims.location && req.result.length < 2) {    // Garantimos que conterá as duas localizações no resultado para o simCAQ
-- 
GitLab


From 55c23553ab77668c746e443552c41e21f083f522 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 26 Apr 2023 10:47:56 -0300
Subject: [PATCH 011/123] FIX MongoDB to PostgresDB AccessToken, RefreshToken,
 Client

---
 src/libs/middlewares/oauth2.js   | 11 +++++++--
 src/libs/middlewares/passport.js | 20 ++++++++++-----
 src/libs/models/accessToken.js   | 42 +++++++++++++++++---------------
 src/libs/models/client.js        | 32 +++++++++++++++---------
 src/libs/models/refreshToken.js  | 42 +++++++++++++++++---------------
 src/libs/models/user.js          |  4 +--
 src/libs/routes_v1/api.js        |  4 +++
 src/libs/routes_v1/test.js       | 33 +++++++++++++++++++++++++
 src/libs/routes_v1/user.js       |  5 ++--
 9 files changed, 129 insertions(+), 64 deletions(-)
 create mode 100644 src/libs/routes_v1/test.js

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 1021c58d..10d3278c 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -11,6 +11,7 @@ const User = require(`${libs}/models/user`);
 const AccessToken = require(`${libs}/models/accessToken`);
 const RefreshToken = require(`${libs}/models/refreshToken`);
 
+
 // create OAuth 2.0 server
 let aserver = oauth2orize.createServer()
 
@@ -57,7 +58,8 @@ let generateTokens = (data, done) => {
 
 // Exchange username & password for access token.
 aserver.exchange(oauth2orize.exchange.password((client, username, password, scope, done) => {
-    User.findOne({ email: username }, (err, user) => {
+    console.log("Exchange");
+    User.findOne({where:{ email: username }}, (err, user) => {
         if (err) {
             return done(err);
         }
@@ -78,6 +80,7 @@ aserver.exchange(oauth2orize.exchange.password((client, username, password, scop
 
 // Exchange refreshToken for access token.
 aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope, done)  =>{
+    console.log("Refresha Token");
     RefreshToken.findOne({ token: refreshToken, clientId: client._id }, (err, token) => {
         if (err) {
             return done(err);
@@ -114,7 +117,11 @@ aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope,
 // authenticate when making requests to this endpoint.
 
 exports.token = [
-    passport.authenticate(['oauth2-client-password'], { session: false }),
+    // ()=>{console.log("C3sl")},
+    passport.authenticate(['oauth2-client-password'], { session: false },function(err, user) {
+        if (err) { console.log("Erro de autenticação"); }
+        if (!user) { console.log("Erro de usuario ausente");}
+    }),
     aserver.token(),
     aserver.errorHandler()
 ];
diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js
index 393e5c46..55e12e3c 100644
--- a/src/libs/middlewares/passport.js
+++ b/src/libs/middlewares/passport.js
@@ -8,41 +8,47 @@ const config = require(`${libs}/config`);
 const User = require(`${libs}/models/user`);
 const Client = require(`${libs}/models/client`);
 const AccessToken = require(`${libs}/models/accessToken`);
-const RefreshToken = require(`${libs}/models/refreshToken`);
-const email = require(`${libs}/middlewares/email`);
+
 
 passport.use(new ClientPasswordStrategy( (clientId, clientSecret, done) => {
-        Client.findOne({ _id: clientId }, (err, client) => {
+        console.log("Entrei no ClientPasswordStrategy");
+        Client.findOne(clientId, (err, client) => {
             if (err) {
+                console.log("Erro de requisicao");
                 return done(err);
             }
 
             if (!client) {
+                console.log("Erro de cliente");
                 return done(null, false);
             }
 
             if (client.clientSecret !== clientSecret) {
+                console.log("Erro de geracao Chave secreta");
                 return done(null, false);
             }
-
+            console.log("Tudo certo nesse use");
             return done(null, client);
         })
-    }
-));
+    }));
 
 passport.use(new BearerStrategy( (accessToken, done) => {
+        console.log("Entrei no BearerStrategy");
         AccessToken.findOne({ token: accessToken }, (err, token) => {
             if (err) {
+                console.log("ERRO AcessToken");
                 return done(err);
             }
 
             if (!token) {
+                console.log("ERRO Token");
                 return done(null, false);
             }
 
             if( Math.round((Date.now()-token.created)/1000) > config.security.tokenLife) {
                 AccessToken.remove({ token: accessToken }, (err) => {
                     if (err) {
+                        console.log("ERRO remove Token")
                         return done(err);
                     }
                 });
@@ -52,10 +58,12 @@ passport.use(new BearerStrategy( (accessToken, done) => {
 
             User.findByPk(token.userId, function(err, usuario) {
                 if (err) {
+                    console.log("ERRO PK");
                     return done(err);
                 }
 
                 if (!usuario) {
+                    console.log("ERRO NAO USUARIO");
                     return done(null, false, { msg: 'Unknown user' });
                 }
 
diff --git a/src/libs/models/accessToken.js b/src/libs/models/accessToken.js
index daab5898..7eedfefa 100644
--- a/src/libs/models/accessToken.js
+++ b/src/libs/models/accessToken.js
@@ -1,29 +1,31 @@
-const mongoose = require('mongoose');
-const Schema = mongoose.Schema;
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js')
 const libs = `${process.cwd()}/libs`;
 const User = require(`${libs}/models/user`);
 const Client = require(`${libs}/models/client`);
 
-let AccessToken = new Schema({
-    userId: {
-        type: Schema.Types.ObjectId,
-        required: true,
-        ref: 'User'
+var AccessToken = db.define("AccessToken",{
+    user_id: {
+        type: Sequelize.STRING,
+        allowNull: false
     },
-    clientId: {
-        type: Schema.Types.ObjectId,
-        required: true,
-        ref: 'Client'
+    client_id:{
+        type: Sequelize.STRING,
+        allowNull: false
     },
-    token: {
-        type: String,
-        unique: true,
-        required: true
+    token:{
+        type: Sequelize.STRING, 
+        allowNull: false,
+        unique: true
     },
-    createdAt: {
-        type: Date,
+    created_at:{
+        type: Sequelize.DATE,
         default: Date.now
-    }
-});
+    }},
+    {timestamps: false}
+);
 
-module.exports = mongoose.model('AccessToken', AccessToken);
+AccessToken.hasOne(User);
+AccessToken.hasOne(Client);
+
+module.exports = AccessToken;
diff --git a/src/libs/models/client.js b/src/libs/models/client.js
index 8ac80d8d..10d7db87 100644
--- a/src/libs/models/client.js
+++ b/src/libs/models/client.js
@@ -1,17 +1,25 @@
-const mongoose = require('mongoose');
-const Schema = mongoose.Schema;
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
 
-let Client = new Schema({
-    name: {
-        type: String,
+
+var Client = db.define("client",{
+    id:{
+        type: Sequelize.STRING,
+        allowNull:false,
         unique: true,
-        required: true
+        primaryKey: true
     },
-    clientSecret: {
-        type: String,
-        required: true,
+    name:{
+        type: Sequelize.STRING,
+        allowNull:false,
         unique: true
-    }
-});
+    },
+    client_secret:{
+        type: Sequelize.STRING,
+        allowNull:false,
+        unique:true
+    },
+},
+{timestamps: false});
 
-module.exports = mongoose.model('Client', Client);
+module.exports = Client;
\ No newline at end of file
diff --git a/src/libs/models/refreshToken.js b/src/libs/models/refreshToken.js
index c5f8fd63..0d59d8ad 100644
--- a/src/libs/models/refreshToken.js
+++ b/src/libs/models/refreshToken.js
@@ -1,30 +1,32 @@
-const mongoose = require('mongoose');
-const Schema = mongoose.Schema;
+const Sequelize = require("sequelize");
 const libs = `${process.cwd()}/libs`;
+const db = require('../db/postgres.js');
 const User = require(`${libs}/models/user`);
 const Client = require(`${libs}/models/client`);
 
-let RefreshToken = new Schema({
-    userId: {
-        type: Schema.Types.ObjectId,
-        required: true,
-        ref: 'User'
+var RefreshToken = db.define("RefreshToken",{
+    user_id: {
+        type: Sequelize.STRING,
+        allowNull: false
     },
-    clientId: {
-        type: Schema.Types.ObjectId,
-        required: true,
-        ref: 'Client'
+    client_id:{
+        type: Sequelize.STRING,
+        allowNull: false
     },
-    token: {
-        type: String,
-        unique: true,
-        required: true
+    token:{
+        type: Sequelize.STRING, 
+        allowNull: false,
+        unique: true
     },
-    createdAt: {
-        type: Date,
+    created_at:{
+        type: Sequelize.DATE,
         default: Date.now
-    }
-});
+    }},
+    {timestamps: false}
+);
 
-module.exports = mongoose.model('RefreshToken', RefreshToken);
+RefreshToken.hasOne(User);
+RefreshToken.hasOne(Client);
+
+module.exports = RefreshToken;
 
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 1d01bcae..139f83ac 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -1,6 +1,6 @@
 const Sequelize = require("sequelize");
-const crypto = require('crypto')
-const db = require('../db/postgres.js')
+const crypto = require('crypto');
+const db = require('../db/postgres.js');
 const libs = `${process.cwd()}/libs`;
 
 // set up a sequelize model 
diff --git a/src/libs/routes_v1/api.js b/src/libs/routes_v1/api.js
index 4b6a5630..41fe392c 100644
--- a/src/libs/routes_v1/api.js
+++ b/src/libs/routes_v1/api.js
@@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License
 along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
 */
 
+
 const express = require('express');
 
 const api = express();
@@ -26,6 +27,8 @@ const libs = `${process.cwd()}/libs`;
 
 const config = require(`${libs}/config`);
 
+const test = require('./test');
+
 const classes = require('./class');
 
 const enrollment = require('./enrollment');
@@ -139,6 +142,7 @@ api.get('/', (req, res) => {
 });
 
 // mount API routes_v1
+api.use('/test', test);
 api.use('/user', user);
 api.use('/simulation', simulation);
 api.use('/class', classes);
diff --git a/src/libs/routes_v1/test.js b/src/libs/routes_v1/test.js
new file mode 100644
index 00000000..a2de966b
--- /dev/null
+++ b/src/libs/routes_v1/test.js
@@ -0,0 +1,33 @@
+const express = require('express');
+
+const testApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const Client = require(`${libs}/models/client`);
+
+
+testApp.get("/client", (clientId, clientSecret, done) => {
+    console.log("Entrei no ClientPasswordStrategy");
+    Client.findOne(clientId, (err, client) => {
+        if (err) {
+            console.log("Erro de requisicao");
+            return done(err);
+        }
+
+        if (!client) {
+            console.log("Erro de cliente");
+            return done(null, false);
+        }
+
+        if (client.clientSecret !== clientSecret) {
+            console.log("Erro de geracao Chave secreta");
+            return done(null, false);
+        }
+        console.log("Tudo certo nesse use");
+        return done(null, client);
+    }).then((result) => res.json(result));
+})
+
+
+module.exports = testApp;
\ No newline at end of file
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 085b697d..3637e671 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -92,6 +92,7 @@ userApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, n
 */
 
 userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+  res.json(req.user);
   let user = req.user.toObject();
   delete user.hashedPassword;
   delete user.salt;
@@ -258,7 +259,7 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, r
 
 userApp.get('/reset/password', (req, res, next) => {
   let emailAddress = req.query.email;
-  User.findOne({email: emailAddress}, (err, user)=> {
+  User.findOne({where:{email: emailAddress}}, (err, user)=> {
     if(err) {
       log.error(err);
       let errors = [];
@@ -298,7 +299,7 @@ userApp.get('/reset/password', (req, res, next) => {
         });
       })
     }
-  })
+  }).then((result) => res.json(result));
 })
 
 module.exports = userApp;
-- 
GitLab


From d309942090123d909100ad4cc0999837cfd03aa3 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 27 Apr 2023 10:13:06 -0300
Subject: [PATCH 012/123] =?UTF-8?q?Momentos=20antes=20de=20toda=20mudan?=
 =?UTF-8?q?=C3=A7a?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/libs/middlewares/oauth2.js |  2 +-
 src/libs/models/resetToken.js  | 44 +++++++++++++++++-----------------
 src/libs/models/user.js        |  2 +-
 src/libs/routes_v1/test.js     | 41 ++++++++++++++++---------------
 4 files changed, 44 insertions(+), 45 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 10d3278c..7babf1a8 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -81,7 +81,7 @@ aserver.exchange(oauth2orize.exchange.password((client, username, password, scop
 // Exchange refreshToken for access token.
 aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope, done)  =>{
     console.log("Refresha Token");
-    RefreshToken.findOne({ token: refreshToken, clientId: client._id }, (err, token) => {
+    RefreshToken.findOne({ token: refreshToken, client_id: client._id }, (err, token) => {
         if (err) {
             return done(err);
         }
diff --git a/src/libs/models/resetToken.js b/src/libs/models/resetToken.js
index 322d5281..540594b9 100644
--- a/src/libs/models/resetToken.js
+++ b/src/libs/models/resetToken.js
@@ -1,33 +1,32 @@
-const mongoose = require('mongoose');
-const Schema = mongoose.Schema;
+const Sequelize = require("sequelize");
 const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
+const db = require('../db/postgres.js');
 const User = require(`${libs}/models/user`);
 const uuid = require('node-uuid');
 
-let ResetToken = new Schema({
-    userId: {
-        type: Schema.Types.ObjectId,
-        required: true,
-        ref: 'User'
+var ResetToken = db.define("ResetToken",{
+    user_id: {
+        type: Sequelize.STRING,
+        allowNull: false
     },
-    token: {
-        type: String,
-        required: true
+    token:{
+        type: Sequelize.STRING, 
+        allowNull: false,
+        unique: true
     },
-    reset: {
-        type: Boolean,
-        required: true,
+    reset:{
+        type: Sequelize.BOOLEAN,
+        allowNull: false,
         default: false
     },
-    createdAt: {
-        type: Date,
-        required: true,
+    created_at:{
+        type: Sequelize.DATE,
         default: Date.now
-    }
-});
+    }},
+    {timestamps: false}
+);
 
-ResetToken.methods.createResetToken = function (done) {
+ResetToken.createResetToken = function (done) {
     let resetToken = this;
     let token = uuid.v4();
     resetToken.set('token', token);
@@ -38,9 +37,10 @@ ResetToken.methods.createResetToken = function (done) {
         return done(null, token);
     })
 }
-ResetToken.methods.hasExpired = function () {
+
+ResetToken.hasExpired = function () {
     var now = new Date();
     return (now - this.createdAt) > 86400; //Expire if token is 1 day old
 };
 
-module.exports = mongoose.model('ResetToken', ResetToken);
+module.exports = ResetToken
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 139f83ac..a07cc68f 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -32,7 +32,7 @@ var User = db.define("User",{
             notNull: { msg: "O campo Senha é obrigatório." },
         },
         get() {
-            return() => this.getDataValue('salt')
+            return() => this.getDataValue('hashed_password')
         }
     },
     salt: {
diff --git a/src/libs/routes_v1/test.js b/src/libs/routes_v1/test.js
index a2de966b..2e529eaa 100644
--- a/src/libs/routes_v1/test.js
+++ b/src/libs/routes_v1/test.js
@@ -1,33 +1,32 @@
 const express = require('express');
-
+const oauth2orize = require('oauth2orize');
+const passport = require('passport');
+const ClientPasswordStrategy = require('passport-oauth2-client-password');
 const testApp = express();
 
 const libs = `${process.cwd()}/libs`;
 
 const Client = require(`${libs}/models/client`);
 
+var server = oauth2orize.createServer();
+
 
-testApp.get("/client", (clientId, clientSecret, done) => {
-    console.log("Entrei no ClientPasswordStrategy");
-    Client.findOne(clientId, (err, client) => {
-        if (err) {
-            console.log("Erro de requisicao");
-            return done(err);
-        }
-
-        if (!client) {
-            console.log("Erro de cliente");
-            return done(null, false);
-        }
-
-        if (client.clientSecret !== clientSecret) {
-            console.log("Erro de geracao Chave secreta");
-            return done(null, false);
-        }
-        console.log("Tudo certo nesse use");
+passport.use(new ClientPasswordStrategy(
+    function(clientId, clientSecret, done) {
+      console.log("ENTREI AQUI NO CLIENTPASSWORDSTRATEGY")
+      Clients.findOne({where:{client_id: clientId }}, function (err, client) {
+        if (err) { return done(err); }
+        if (!client) { return done(null, false); }
+        if (client.clientSecret != clientSecret) { return done(null, false); }
         return done(null, client);
-    }).then((result) => res.json(result));
-})
+      });
+    }
+  ));
+
+
+testApp.get('/profile',
+            passport.authenticate(['oauth2-client-password'], { session: false }),
+            server.token());
 
 
 module.exports = testApp;
\ No newline at end of file
-- 
GitLab


From 8855ab6af8349a18448827309f6793bb7c78d4f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 27 Apr 2023 11:02:11 -0300
Subject: [PATCH 013/123] Add simcaq aggregated enrollment

---
 src/libs/routes_v2/api.js                     |   3 +
 .../routes_v2/simcaqAggregatedEnrollment.js   | 211 ++++++++++++++++++
 2 files changed, 214 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqAggregatedEnrollment.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 25c829f9..2072e864 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -156,6 +156,8 @@ const simcaqNumberOfTeachers = require(`${libs}/routes_v2/simcaqNumberOfTeachers
 
 const simcaqEnrollmentProjection = require(`${libs}/routes_v2/simcaqEnrollmentProjection`);
 
+const simcaqAggregatedEnrollment = require(`${libs}/routes_v2/simcaqAggregatedEnrollment`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -225,5 +227,6 @@ api.use('/simcaq_new_classes', simcaqNewClasses);
 api.use('/simcaq_result', simcaqResult);
 api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
 api.use('/simcaq_enrollment_projection', simcaqEnrollmentProjection);
+api.use('/simcaq_aggregated_enrollment', simcaqAggregatedEnrollment);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqAggregatedEnrollment.js b/src/libs/routes_v2/simcaqAggregatedEnrollment.js
new file mode 100644
index 00000000..7562acfa
--- /dev/null
+++ b/src/libs/routes_v2/simcaqAggregatedEnrollment.js
@@ -0,0 +1,211 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqAggregatedEnrollmentApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqAggregatedEnrollmentApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_matricula_agregada'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_matricula_agregada'
+    }
+}, 'dims').addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'simcaq_matricula_agregada'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_matricula_agregada'
+    }
+}, 'dims').addValue({
+    name: 'locale',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'localizacao_id',
+    resultField: 'locale_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'adm_dependency_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'diff_location_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'school_building',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'local_func_predio_escolar',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'local_func_predio_escolar'
+    }
+}).addValue({
+    name: 'rural_location_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'localidade_area_rural',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'school_year_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'serie_ano_id',
+    resultField: 'school_year_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'serie_ano_id'
+    }
+}).addValue({
+    name: 'education_level_short_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+});
+
+simcaqAggregatedEnrollmentApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_matricula_agregada')
+        .field('SUM(simcaq_matricula_agregada.num_matriculas)', 'num_enrollments')
+        .field('simcaq_matricula_agregada.ano_censo', 'year')
+        .group('simcaq_matricula_agregada.ano_censo');
+    next();
+}, query, id2str.transform(), response('aggregatedEnrollment'));
+
+module.exports = simcaqAggregatedEnrollmentApp;
-- 
GitLab


From 6b93ae7e76a2327dc031273b3e29dc715dd8b068 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 28 Apr 2023 09:55:08 -0300
Subject: [PATCH 014/123] Add teacher city plan route

---
 src/libs/routes_v2/api.js                   |   3 +
 src/libs/routes_v2/simcaqTeacherCityPlan.js | 139 ++++++++++++++++++++
 2 files changed, 142 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqTeacherCityPlan.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 2072e864..5c00d3fe 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -158,6 +158,8 @@ const simcaqEnrollmentProjection = require(`${libs}/routes_v2/simcaqEnrollmentPr
 
 const simcaqAggregatedEnrollment = require(`${libs}/routes_v2/simcaqAggregatedEnrollment`);
 
+const simcaqTeacherCityPlan = require(`${libs}/routes_v2/simcaqTeacherCityPlan`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -228,5 +230,6 @@ api.use('/simcaq_result', simcaqResult);
 api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
 api.use('/simcaq_enrollment_projection', simcaqEnrollmentProjection);
 api.use('/simcaq_aggregated_enrollment', simcaqAggregatedEnrollment);
+api.use('/simcaq_teacher_city_plan', simcaqTeacherCityPlan);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqTeacherCityPlan.js b/src/libs/routes_v2/simcaqTeacherCityPlan.js
new file mode 100644
index 00000000..72c19913
--- /dev/null
+++ b/src/libs/routes_v2/simcaqTeacherCityPlan.js
@@ -0,0 +1,139 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqTeacherCityPlanApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqTeacherCityPlanApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_docente_municipio_plano',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'simcaq_docente_municipio_plano'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_estado_id',
+        foreignTable: 'simcaq_docente_municipio_plano'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: ['nome', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_docente_municipio_plano'
+    }
+}, 'dims').addValue({
+    name: 'country',
+    table: 'simcaq_docente_municipio_plano',
+    tableField: ['escola_pais_nome', 'escola_pais_id'],
+    resultField: ['country_name', 'country_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'escola_pais_id'
+    }
+}).addValue({
+    name: 'adm_dependency_id',
+    table: 'simcaq_docente_municipio_plano',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+});
+
+simcaqTeacherCityPlanApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_docente_municipio_plano')
+        .field('simcaq_docente_municipio_plano.num_docentes', 'num_teachers')
+        .field('simcaq_docente_municipio_plano.dependencia_adm_id', 'adm_dependency_id')
+        .field('simcaq_docente_municipio_plano.ano_censo', 'year');
+    next();
+}, query, id2str.transform(), response('simcaqTeacherCityPlan'));
+
+module.exports = simcaqTeacherCityPlanApp;
-- 
GitLab


From d8e84866f6fb90dc23ef2316d0fc1a978f6176f9 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Tue, 2 May 2023 15:53:07 -0300
Subject: [PATCH 015/123] Passport not unauthorized anymore

---
 package.json                     |   1 +
 src/libs/middlewares/oauth2.js   | 100 +++++++++++++------------------
 src/libs/middlewares/passport.js |  54 +++++++----------
 src/libs/models/accessToken.js   |   4 +-
 src/libs/models/client.js        |   1 -
 src/libs/models/refreshToken.js  |   4 +-
 src/libs/routes_v1/test.js       |  94 +++++++++++++++++++++++++----
 7 files changed, 146 insertions(+), 112 deletions(-)

diff --git a/package.json b/package.json
index 54eb00db..94ccf471 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
     "babel-core": "^6.26.3",
     "babel-preset-es2015": "^6.24.1",
     "babel-register": "^6.26.0",
+    "bcrypt": "^5.1.0",
     "bcrypt-nodejs": "0.0.3",
     "body-parser": "^1.18.3",
     "chai": "^3.5.0",
diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 7babf1a8..37ac3509 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -23,88 +23,65 @@ let errFn = (cb, err) => {
 }
 
 // Destroys any old tokens and generates a new access and refresh token
-let generateTokens = (data, done) => {
+let generateTokens = (userId, clientId, done) => {
     // curries in `done` callback so we don't need to pass it
-    let errorHandler = errFn.bind(undefined, done);
-    let refreshToken;
     let refreshTokenValue;
     let token;
     let tokenValue;
 
-    RefreshToken.remove(data, errorHandler);
-    AccessToken.remove(data, errorHandler);
+    RefreshToken.destroy({where:{"user_id": userId, "client_id": clientId}});
+    AccessToken.destroy({where:{"user_id": userId, "client_id": clientId}});
 
     tokenValue = crypto.randomBytes(32).toString('hex');
     refreshTokenValue = crypto.randomBytes(32).toString('hex');
 
-    data.token = tokenValue;
-    token = new AccessToken(data);
-
-    data.token = refreshTokenValue;
-    refreshToken = new RefreshToken(data);
+    AccessToken.create({
+        user_id:userId, 
+        client_id:clientId,
+        token:tokenValue
+    })
 
-    refreshToken.save(errorHandler);
+    let refreshed_token = refreshTokenValue;
 
-    token.save((err) => {
-        if (err) {
-            log.error(err);
-            return done(err);
-        }
-        done(null, tokenValue, refreshTokenValue, {
-            'expires_in': config.security.tokenLife
-        });
+    RefreshToken.create({
+        user_id:userId, 
+        client_id:clientId,
+        token:refreshed_token
     })
 };
 
-// Exchange username & password for access token.
-aserver.exchange(oauth2orize.exchange.password((client, username, password, scope, done) => {
-    console.log("Exchange");
-    User.findOne({where:{ email: username }}, (err, user) => {
-        if (err) {
-            return done(err);
-        }
-
-        if (!user || !user.checkPassword(password)) {
-            return done(null, false);
-        }
 
-        var model = {
-            userId: user._id,
-            clientId: client._id
-        };
-        log.info(`Gerando token para usuário ${user.name}`);
-        generateTokens(model, done);
-    })
+aserver.exchange(oauth2orize.exchange.password(function(client, username, password, scope, done) {
+    User.findOne({ 
+      where: {email:username} 
+    }).then(function(user) {
+      if(user == null){
+        return done(null, false);
+      }
+      if(user.dataValues.origin != client.client_secret){
+        console.log("Erro de client_secret");
+        return done(null, false);
+      }
+      log.info(`Gerando token para usuário ${user.name}`);
+      generateTokens(user.dataValues.id, client.id, done);
+    }).catch(function(error) {
+      return done(error);
+    });
+  }));
 
-}));
 
 // Exchange refreshToken for access token.
 aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope, done)  =>{
     console.log("Refresha Token");
-    RefreshToken.findOne({ token: refreshToken, client_id: client._id }, (err, token) => {
-        if (err) {
-            return done(err);
-        }
-
+    RefreshToken.findOne({where: {token: refreshToken, client_id: client.id }}).then(function(token){
         if (!token) {
             return done(null, false);
         }
-
-        User.findByPk(token.userId , (err, user)  => {
-            if (err) { 
-                log.error(err);
-                return done(err);
-            }
+        User.findByPk(token.user_id).then(function(user){
             if (!user) { 
                 return done(null, false); 
             }
-
-            var model = {
-                userId: user._id,
-                clientId: client._id
-            };
-
-            generateTokens(model, done);
+            generateTokens(user.id, client.id, done);
         })
     })
 }))
@@ -116,12 +93,15 @@ aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope,
 // exchange middleware will be invoked to handle the request.  Clients must
 // authenticate when making requests to this endpoint.
 
+
+// ,function(err, user) {
+//     if (err) { console.log("Erro de autenticação"); }
+//     if (!user) { console.log("Erro de usuario ausente");}
+// }
+
 exports.token = [
     // ()=>{console.log("C3sl")},
-    passport.authenticate(['oauth2-client-password'], { session: false },function(err, user) {
-        if (err) { console.log("Erro de autenticação"); }
-        if (!user) { console.log("Erro de usuario ausente");}
-    }),
+    passport.authenticate(['oauth2-client-password'], { session: false }),
     aserver.token(),
     aserver.errorHandler()
 ];
diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js
index 55e12e3c..28e07e32 100644
--- a/src/libs/middlewares/passport.js
+++ b/src/libs/middlewares/passport.js
@@ -10,36 +10,28 @@ const Client = require(`${libs}/models/client`);
 const AccessToken = require(`${libs}/models/accessToken`);
 
 
-passport.use(new ClientPasswordStrategy( (clientId, clientSecret, done) => {
-        console.log("Entrei no ClientPasswordStrategy");
-        Client.findOne(clientId, (err, client) => {
-            if (err) {
-                console.log("Erro de requisicao");
-                return done(err);
-            }
-
-            if (!client) {
-                console.log("Erro de cliente");
-                return done(null, false);
-            }
-
-            if (client.clientSecret !== clientSecret) {
-                console.log("Erro de geracao Chave secreta");
-                return done(null, false);
-            }
-            console.log("Tudo certo nesse use");
-            return done(null, client);
-        })
-    }));
+passport.use(new ClientPasswordStrategy(
+    function(client_id, client_secret, done) {
+      Client.findOne({where: {id: client_id} 
+      }).then(function(client) {
+        if(!client){
+            console.log("Erro de cliente");
+            return done(null, false);
+        }
+        if (client.client_secret !== client_secret){
+            console.log("Erro de geracao Chave Secreta");
+            return done(null, false);
+        } 
+        return done(null, client);
+      }).catch(function(error) {
+        return done(error);
+      });
+    }
+  ));
 
 passport.use(new BearerStrategy( (accessToken, done) => {
         console.log("Entrei no BearerStrategy");
-        AccessToken.findOne({ token: accessToken }, (err, token) => {
-            if (err) {
-                console.log("ERRO AcessToken");
-                return done(err);
-            }
-
+        AccessToken.findOne({where:{token: accessToken}}, (token) => {
             if (!token) {
                 console.log("ERRO Token");
                 return done(null, false);
@@ -56,17 +48,11 @@ passport.use(new BearerStrategy( (accessToken, done) => {
                 return done(null, false, { msg: 'Token expired' });
             }
 
-            User.findByPk(token.userId, function(err, usuario) {
-                if (err) {
-                    console.log("ERRO PK");
-                    return done(err);
-                }
-
+            User.findByPk(token.userId, function(usuario) {
                 if (!usuario) {
                     console.log("ERRO NAO USUARIO");
                     return done(null, false, { msg: 'Unknown user' });
                 }
-
                 var info = { scope: '*' };
                 done(null, usuario, info);
             })
diff --git a/src/libs/models/accessToken.js b/src/libs/models/accessToken.js
index 7eedfefa..68f6802a 100644
--- a/src/libs/models/accessToken.js
+++ b/src/libs/models/accessToken.js
@@ -25,7 +25,7 @@ var AccessToken = db.define("AccessToken",{
     {timestamps: false}
 );
 
-AccessToken.hasOne(User);
-AccessToken.hasOne(Client);
+AccessToken.hasOne(User, { foreignKey: 'id' });
+AccessToken.hasOne(Client, { foreignKey: 'id' });
 
 module.exports = AccessToken;
diff --git a/src/libs/models/client.js b/src/libs/models/client.js
index 10d7db87..b4eabcbc 100644
--- a/src/libs/models/client.js
+++ b/src/libs/models/client.js
@@ -1,7 +1,6 @@
 const Sequelize = require("sequelize");
 const db = require('../db/postgres.js');
 
-
 var Client = db.define("client",{
     id:{
         type: Sequelize.STRING,
diff --git a/src/libs/models/refreshToken.js b/src/libs/models/refreshToken.js
index 0d59d8ad..01b33ac8 100644
--- a/src/libs/models/refreshToken.js
+++ b/src/libs/models/refreshToken.js
@@ -25,8 +25,8 @@ var RefreshToken = db.define("RefreshToken",{
     {timestamps: false}
 );
 
-RefreshToken.hasOne(User);
-RefreshToken.hasOne(Client);
+RefreshToken.hasOne(User, { foreignKey: 'id' });
+RefreshToken.hasOne(Client, { foreignKey: 'id' });
 
 module.exports = RefreshToken;
 
diff --git a/src/libs/routes_v1/test.js b/src/libs/routes_v1/test.js
index 2e529eaa..9e8482dc 100644
--- a/src/libs/routes_v1/test.js
+++ b/src/libs/routes_v1/test.js
@@ -3,30 +3,98 @@ const oauth2orize = require('oauth2orize');
 const passport = require('passport');
 const ClientPasswordStrategy = require('passport-oauth2-client-password');
 const testApp = express();
-
 const libs = `${process.cwd()}/libs`;
 
 const Client = require(`${libs}/models/client`);
-
+const User = require(`${libs}/models/user`)
 var server = oauth2orize.createServer();
 
-
 passport.use(new ClientPasswordStrategy(
-    function(clientId, clientSecret, done) {
-      console.log("ENTREI AQUI NO CLIENTPASSWORDSTRATEGY")
-      Clients.findOne({where:{client_id: clientId }}, function (err, client) {
-        if (err) { return done(err); }
-        if (!client) { return done(null, false); }
-        if (client.clientSecret != clientSecret) { return done(null, false); }
+    function(client_id, client_secret, done) {
+      Client.findOne({where: {client_id: client_id} 
+      }).then(function(err, client) {
+        if(err){
+            console.log("Erro de requisicao");
+            return done(err);
+        }
+        if(!client){
+            console.log("Erro de cliente");
+            return done(null, false);
+        }
+        if (client.client_secret !== client_secret){
+            console.log("Erro de geracao Chave Secreta");
+            return done(null, false);
+        } 
+        console.log("Tudo certo nesse use");
         return done(null, client);
-      });
+      })
     }
   ));
 
+let generateTokens = (userId, clientId, done) => {
+    // curries in `done` callback so we don't need to pass it
+    let refreshTokenValue;
+    let token;
+    let tokenValue;
+
+    RefreshToken.destroy({where:{"user_id": userId, "client_id": clientId}});
+    AccessToken.destroy({where:{"user_id": userId, "client_id": clientId}});
+
+    tokenValue = crypto.randomBytes(32).toString('hex');
+    refreshTokenValue = crypto.randomBytes(32).toString('hex');
+
+    AccessToken.create({
+        user_id:userId, 
+        client_id:clientId,
+        token:tokenValue
+    })
+
+    let refreshed_token = refreshTokenValue;
+
+    RefreshToken.create({
+        user_id:userId, 
+        client_id:clientId,
+        token:refreshed_token
+    })
+
+    token.save((err) => {
+        if (err) {
+            log.error(err);
+            return done(err);
+        }
+        done(null, tokenValue, refreshTokenValue, {
+            'expires_in': config.security.tokenLife
+        });
+    })
+};
+
+
+let entrar = function(client, username, done) {
+    User.findOne({ 
+      where: {email:username} 
+    }).then(function(user) {
+      console.log(user)
+      if(user == null){
+        return done(null, false);
+      }
+      if(user.dataValues.origin != client.client_secret){
+        console.log("Erro de client_secret");
+        return done(null, false);
+      }
+      log.info(`Gerando token para usuário ${user.name}`);
+      generateTokens(user._id, client._id, done);
+    }).catch(function(error) {
+      return done(error);
+    });
+  };
+
+
+
+
 
-testApp.get('/profile',
-            passport.authenticate(['oauth2-client-password'], { session: false }),
-            server.token());
+testApp.post('/', (req, res, next) =>{
+    entrar(req.body, req.body.username);
+});
 
 
 module.exports = testApp;
\ No newline at end of file
-- 
GitLab


From 87ea2747e4ffa85f797cd29d828f36da8f01f676 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 3 May 2023 09:35:19 -0300
Subject: [PATCH 016/123] Authentication ok!

---
 src/libs/middlewares/oauth2.js  | 6 +++++-
 src/libs/models/accessToken.js  | 3 ++-
 src/libs/models/refreshToken.js | 3 ++-
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 37ac3509..775a759a 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -26,7 +26,6 @@ let errFn = (cb, err) => {
 let generateTokens = (userId, clientId, done) => {
     // curries in `done` callback so we don't need to pass it
     let refreshTokenValue;
-    let token;
     let tokenValue;
 
     RefreshToken.destroy({where:{"user_id": userId, "client_id": clientId}});
@@ -48,6 +47,11 @@ let generateTokens = (userId, clientId, done) => {
         client_id:clientId,
         token:refreshed_token
     })
+
+    done(null, tokenValue, refreshTokenValue, {
+        'expires_in': config.security.tokenLife
+    });
+
 };
 
 
diff --git a/src/libs/models/accessToken.js b/src/libs/models/accessToken.js
index 68f6802a..2970b6fa 100644
--- a/src/libs/models/accessToken.js
+++ b/src/libs/models/accessToken.js
@@ -16,7 +16,8 @@ var AccessToken = db.define("AccessToken",{
     token:{
         type: Sequelize.STRING, 
         allowNull: false,
-        unique: true
+        unique: true,
+        primaryKey: true
     },
     created_at:{
         type: Sequelize.DATE,
diff --git a/src/libs/models/refreshToken.js b/src/libs/models/refreshToken.js
index 01b33ac8..ff73bd3e 100644
--- a/src/libs/models/refreshToken.js
+++ b/src/libs/models/refreshToken.js
@@ -16,7 +16,8 @@ var RefreshToken = db.define("RefreshToken",{
     token:{
         type: Sequelize.STRING, 
         allowNull: false,
-        unique: true
+        unique: true,
+        primaryKey: true
     },
     created_at:{
         type: Sequelize.DATE,
-- 
GitLab


From cc40f21cd70435e641364a7e0b5a95e48c30b848 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 4 May 2023 11:37:05 -0300
Subject: [PATCH 017/123] [FIX] reset password path

---
 src/libs/middlewares/oauth2.js       |   2 -
 src/libs/middlewares/passport.js     |   6 +-
 src/libs/models/accessToken.js       |   2 +-
 src/libs/models/refreshToken.js      |   2 +-
 src/libs/models/resetToken.js        |  19 +-
 src/libs/models/user.js              |   6 +-
 src/libs/models/verificationToken.js |  22 +-
 src/libs/routes_v1/user.js           | 309 ++++++++++++++-------------
 8 files changed, 175 insertions(+), 193 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 775a759a..672c203e 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -76,7 +76,6 @@ aserver.exchange(oauth2orize.exchange.password(function(client, username, passwo
 
 // Exchange refreshToken for access token.
 aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope, done)  =>{
-    console.log("Refresha Token");
     RefreshToken.findOne({where: {token: refreshToken, client_id: client.id }}).then(function(token){
         if (!token) {
             return done(null, false);
@@ -104,7 +103,6 @@ aserver.exchange(oauth2orize.exchange.refreshToken((client, refreshToken, scope,
 // }
 
 exports.token = [
-    // ()=>{console.log("C3sl")},
     passport.authenticate(['oauth2-client-password'], { session: false }),
     aserver.token(),
     aserver.errorHandler()
diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js
index 28e07e32..93f5fe0a 100644
--- a/src/libs/middlewares/passport.js
+++ b/src/libs/middlewares/passport.js
@@ -31,14 +31,14 @@ passport.use(new ClientPasswordStrategy(
 
 passport.use(new BearerStrategy( (accessToken, done) => {
         console.log("Entrei no BearerStrategy");
-        AccessToken.findOne({where:{token: accessToken}}, (token) => {
+        AccessToken.findOne({where:{token: accessToken}}).then((token) => {
             if (!token) {
                 console.log("ERRO Token");
                 return done(null, false);
             }
 
             if( Math.round((Date.now()-token.created)/1000) > config.security.tokenLife) {
-                AccessToken.remove({ token: accessToken }, (err) => {
+                AccessToken.destroy({ token: accessToken }).then((err) => {
                     if (err) {
                         console.log("ERRO remove Token")
                         return done(err);
@@ -48,7 +48,7 @@ passport.use(new BearerStrategy( (accessToken, done) => {
                 return done(null, false, { msg: 'Token expired' });
             }
 
-            User.findByPk(token.userId, function(usuario) {
+            User.findByPk(token.user_id).then(function(usuario) {
                 if (!usuario) {
                     console.log("ERRO NAO USUARIO");
                     return done(null, false, { msg: 'Unknown user' });
diff --git a/src/libs/models/accessToken.js b/src/libs/models/accessToken.js
index 2970b6fa..54e4a4ba 100644
--- a/src/libs/models/accessToken.js
+++ b/src/libs/models/accessToken.js
@@ -21,7 +21,7 @@ var AccessToken = db.define("AccessToken",{
     },
     created_at:{
         type: Sequelize.DATE,
-        default: Date.now
+        defaultValue: Date.now
     }},
     {timestamps: false}
 );
diff --git a/src/libs/models/refreshToken.js b/src/libs/models/refreshToken.js
index ff73bd3e..aebdd9b2 100644
--- a/src/libs/models/refreshToken.js
+++ b/src/libs/models/refreshToken.js
@@ -21,7 +21,7 @@ var RefreshToken = db.define("RefreshToken",{
     },
     created_at:{
         type: Sequelize.DATE,
-        default: Date.now
+        defaultValue: Date.now
     }},
     {timestamps: false}
 );
diff --git a/src/libs/models/resetToken.js b/src/libs/models/resetToken.js
index 540594b9..5398e9f4 100644
--- a/src/libs/models/resetToken.js
+++ b/src/libs/models/resetToken.js
@@ -12,32 +12,21 @@ var ResetToken = db.define("ResetToken",{
     token:{
         type: Sequelize.STRING, 
         allowNull: false,
-        unique: true
+        unique: true,
+        primaryKey: true
     },
     reset:{
         type: Sequelize.BOOLEAN,
         allowNull: false,
-        default: false
+        defaultValue: false
     },
     created_at:{
         type: Sequelize.DATE,
-        default: Date.now
+        defaultValue: Date.now
     }},
     {timestamps: false}
 );
 
-ResetToken.createResetToken = function (done) {
-    let resetToken = this;
-    let token = uuid.v4();
-    resetToken.set('token', token);
-    resetToken.reset = false;
-    resetToken.save(function(err) {
-        if (err)
-            return done(err);
-        return done(null, token);
-    })
-}
-
 ResetToken.hasExpired = function () {
     var now = new Date();
     return (now - this.createdAt) > 86400; //Expire if token is 1 day old
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index a07cc68f..25fd8abb 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -134,7 +134,7 @@ var User = db.define("User",{
     },
     created_at:{
         type: Sequelize.DATE,
-        default: Date.now
+        defaultValue: Date.now
     },
     origin:{
         type: Sequelize.ENUM("LDE", "SimCAQ", "MAPFOR"),
@@ -145,7 +145,7 @@ var User = db.define("User",{
     },
     verified:{
         type: Sequelize.BOOLEAN,
-        default:false
+        defaultValue:false
     },
     citesegment:{
         type: Sequelize.STRING
@@ -155,7 +155,7 @@ var User = db.define("User",{
     },
     admin:{
         type: Sequelize.BOOLEAN,
-        default:false
+        defaultValue:false
     },
 },
     {timestamps: false}
diff --git a/src/libs/models/verificationToken.js b/src/libs/models/verificationToken.js
index f6c57a05..9038cac1 100644
--- a/src/libs/models/verificationToken.js
+++ b/src/libs/models/verificationToken.js
@@ -4,38 +4,28 @@ const User = require(`./user.js`);
 const uuid = require('node-uuid');
 
 var VerificationToken = db.define("VerificationToken",{
-    userId: {
+    user_id: {
         type: Sequelize.STRING, 
         allowNull: false
     },
     token:{
         type: Sequelize.STRING,
-        allowNull: false
+        allowNull: false,
+        primaryKey: true
     },
     verified:{
         type: Sequelize.BOOLEAN,
         allowNull: false,
-        default: false
+        defaultValue: false
     },
     created_at:{
         type: Sequelize.DATE,
         allowNull: false,
-        default: Date.now
+        defaultValue: Date.now
     }
 },
 {timestamps: false});
 
-VerificationToken.createVerificationToken = function(done) {
-    let verificationToken = this;
-    let token = uuid.v4();
-    verificationToken.set('token', token);
-    verificationToken.verified = false;
-    verificationToken.save(function(err) {
-        if (err) return done(err);
-        return done(null, token);
-    })
-}
-
-VerificationToken.belongsTo(User);
+VerificationToken.hasOne(User,{ foreignKey: 'id' });
 
 module.exports = VerificationToken;
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 3637e671..ebe3191a 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -20,6 +20,8 @@ const email = require(`${libs}/middlewares/email`);
 
 const passport = require('passport');
 
+const uuid = require('node-uuid');
+
 function emailSyntax(email) {
   const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
   return regex.test(email);
@@ -56,16 +58,16 @@ userApp.get('/segment', (req, res, next) => {
 
 userApp.get('/role', (req, res, next) => {
   req.result = [
-    {"Gestores e equipe gestora das secretarias e ministério da Educação" : ["Dirigente municipal, estadual e federal", "Secretário do MEC", "Servidor da área de planejamento educacional", "Membro de associação de gestores (Ex. Undime, Consed, etc)", "Outro [citar função]"]},
-    {"Gestores dos órgãos de planejamento e finanças (das três esferas de governo)" : ["Equipe gestora dos órgãos de planejamento", "Equipe gestora dos órgãos de finanças", "Outro [citar função]"]},
-    {"Agentes do poder legislativo" : ["Parlamentar", "Assessor/a parlamentar", "Auditor/a dos tribunais de conta", "Conselheiro/a de tribunais de conta.", "Outro [citar função]"]},
-    {"Agentes dos conselhos de educação" : ["Conselheiro/a municipais, estaduais e federais", "Conselheiro/a do Fundeb", "Outro [citar função]"]},
-    {"Profissionais da educação" : ["Professor/a da Educação Básica", "Profissional da educação não-docente", "Outro [citar função]"]},
-    {"Sindicato" : ["Agente de sindicatos"]},
-    {"Sociedade civil interessada no financiamento da Educação Básica de qualidade" : ["Membro de fóruns educacionais", "Membro de ONGs e demais entidades sem fins lucrativos", "Estudante da educação básica e membro de entidades estudantis", "Pais e membros de entidades de pais", "Outro [citar função]"]},
-    {"Comunidade acadêmica" : ["Pesquisador/a", "Estudantes de graduação e pós-graduação", "Representantes de entidades de pesquisa (Ex.: ANPED, ANPAE e FINEDUCA)", "Outro [citar função]"]},
-    {"Imprensa" : ["Jornalista", "Outro [citar função]"]},
-    {"Outro [citar segmento]" : []}
+    { "Gestores e equipe gestora das secretarias e ministério da Educação": ["Dirigente municipal, estadual e federal", "Secretário do MEC", "Servidor da área de planejamento educacional", "Membro de associação de gestores (Ex. Undime, Consed, etc)", "Outro [citar função]"] },
+    { "Gestores dos órgãos de planejamento e finanças (das três esferas de governo)": ["Equipe gestora dos órgãos de planejamento", "Equipe gestora dos órgãos de finanças", "Outro [citar função]"] },
+    { "Agentes do poder legislativo": ["Parlamentar", "Assessor/a parlamentar", "Auditor/a dos tribunais de conta", "Conselheiro/a de tribunais de conta.", "Outro [citar função]"] },
+    { "Agentes dos conselhos de educação": ["Conselheiro/a municipais, estaduais e federais", "Conselheiro/a do Fundeb", "Outro [citar função]"] },
+    { "Profissionais da educação": ["Professor/a da Educação Básica", "Profissional da educação não-docente", "Outro [citar função]"] },
+    { "Sindicato": ["Agente de sindicatos"] },
+    { "Sociedade civil interessada no financiamento da Educação Básica de qualidade": ["Membro de fóruns educacionais", "Membro de ONGs e demais entidades sem fins lucrativos", "Estudante da educação básica e membro de entidades estudantis", "Pais e membros de entidades de pais", "Outro [citar função]"] },
+    { "Comunidade acadêmica": ["Pesquisador/a", "Estudantes de graduação e pós-graduação", "Representantes de entidades de pesquisa (Ex.: ANPED, ANPAE e FINEDUCA)", "Outro [citar função]"] },
+    { "Imprensa": ["Jornalista", "Outro [citar função]"] },
+    { "Outro [citar segmento]": [] }
   ]
   next();
 }, response('role'));
@@ -101,111 +103,101 @@ userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, re
 }, response('user'));
 
 userApp.get('/:id', (req, res, next) => {
-  User.findByPk(req.params.id, (err, user) => {
-    if(err) {
-      log.error(err);
-      return next(err);
-    }
-    if(!user) {
-      req.statusCode = 404;
-      next({msg: 'User not found'});
+  User.findByPk(req.params.id).then((user) => {
+    if (!user) {
+      res.statusCode = 404;
+      res.json({ msg: "O usuário não está cadastrado" });
     } else {
       let u = user.toObject;
       delete u.hashedPassword;
       delete u.salt;
       req.result = u;
     }
-  }).then((result) => res.json(result));
-});
-
-userApp.post('/', (req, res, next) => {
-  let user = new User({
-    email: req.body.email,
-    password: req.body.password,
-    name: req.body.name,
-    nickname: req.body.nickname,
-    cpf: req.body.cpf,
-    cep: req.body.cep,
-    complement: req.body.complement,
-    address: req.body.address,
-    phone: req.body.phone,
-    schooling: req.body.schooling,
-    course: req.body.course,
-    segment: req.body.segment,
-    role: req.body.role,
-    institutionName: req.body.institutionName,
-    state: req.body.state,
-    city: req.body.city,
-    receiveEmails: false || req.body.receiveEmails,
-    origin: req.body.origin,
-    citesegment: req.body.citesegment,
-    citerole: req.body.citerole,
-    admin: false
+  }).catch(function (err) {
+    log.error(err);
+    return next(err);
   });
+});
 
+userApp.post('/', async (req, res, next) => {
   if (typeof req.body.password === 'undefined' || !req.body.password) {
     res.statusCode = 400;
-    return res.json({errors: ["O campo senha é obrigatório"]});
+    return res.json({ errors: ["O campo senha é obrigatório"] });
   } else {
-    user.save((err) => {
-      if(err) {
+    let user = await User.create({
+      email: req.body.email,
+      password: req.body.password,
+      name: req.body.name,
+      nickname: req.body.nickname,
+      cpf: req.body.cpf,
+      cep: req.body.cep,
+      complement: req.body.complement,
+      address: req.body.address,
+      phone: req.body.phone,
+      schooling: req.body.schooling,
+      course: req.body.course,
+      segment: req.body.segment,
+      role: req.body.role,
+      institutionName: req.body.institutionName,
+      state: req.body.state,
+      city: req.body.city,
+      receiveEmails: false || req.body.receiveEmails,
+      origin: req.body.origin,
+      citesegment: req.body.citesegment,
+      citerole: req.body.citerole,
+      admin: false
+    }).catch(function (err) {
+      log.error(err);
+      let errors = [];
+      for (let errName in err.errors) {
+        errors.push(err.errors[errName].message);
+      }
+      log.error(errors);
+      res.statusCode = 400;
+      return res.json({ err, errors });
+      // handle error;
+    });
+    let tokenValue = uuid.v4();
+    const verificationToken = VerificationToken.create({
+      user_id: user.id,
+      token: tokenValue,
+      verified: false
+    });
+    if (!verificationToken) {
+      res.statusCode = 404;
+      return res.json({ msg: "Couldn't create Verification Token" });
+    }
+    let url = config.default.lde.url + '/verify';
+    let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${tokenValue}`;
+    // Send confirmation email
+    let mailOptions = {
+      to: `"${user.name} <${user.email}>"`,
+      subject: "Confirme seu cadastro - Laboratório de Dados Educacionais",
+      text
+    }
+    email(mailOptions, (err, info) => {
+      if (err) {
         log.error(err);
-        let errors = [];
-        for(let errName in err.errors) {
-          errors.push(err.errors[errName].message);
-        }
-        log.error(errors);
-        res.statusCode = 400;
-        return res.json({err, errors});
+        res.json({ msg: 'Message not delivered, user created but not confirmed' });
       }
-
-      // Create verification token
-      let verificationToken = new VerificationToken({
-        userId: user.id
-      });
-
-      verificationToken.createVerificationToken((err, token) => {
-        if(err) {
-          log.error(err);
-          return next(err);
-        }
-        let url = config.default.lde.url + '/verify';
-        let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${token}`;
-        // Send confirmation email
-        let mailOptions = {
-          to: `"${user.name} <${user.email}>"`,
-          subject: "Confirme seu cadastro - Laboratório de Dados Educacionais",
-          text
-        }
-        email(mailOptions, (err, info) => {
-          if(err) {
-            log.error(err);
-            res.json({msg: 'User created'});
-          }
-          if(info) {
-            log.info(`Message ${info.messageId} sent: ${info.response}`);
-            log.info(`Usuário ${user.email} foi criado`);
-          }
-          res.json({msg: 'User created'});
-        });
-      });
+      if (info) {
+        log.info(`Message ${info.messageId} sent: ${info.response}`);
+        log.info(`Usuário ${user.email} foi criado`);
+      }
+      res.json({ msg: 'User created' });
     });
   }
-
 });
 
 userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
-  User.findByPk(req.params.id, (err, user) => {
-    if (err) {
-      log.error(err);
-      return next({err});
-    }
-
-    if(!user) {
+  User.findByPk(req.params.id).then((user) => {
+    if (!user) {
       res.statusCode = 404;
-      return next({err: {
-        message: 'Usuário não encontrado'
-      }});
+      return next({
+        err: {
+          message: 'Usuário não encontrado'
+        }
+      });
     }
 
     user.email = req.body.email || user.email;
@@ -227,79 +219,92 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, r
     user.citerole = req.body.citerole || user.citerole;
 
     if ((req.body.password) && (req.body.newpassword)) {
-        if (req.body.password != req.body.newpassword) {
-            if (user.checkPassword(req.body.password)) {
-                user.password = req.body.newpassword;
-            } else {
-                res.statusCode = 500;
-                return res.json({error: {
-                    message: 'A senha atual está incorreta'
-                }});
-            }
+      if (req.body.password != req.body.newpassword) {
+        if (user.checkPassword(req.body.password)) {
+          user.password = req.body.newpassword;
         } else {
-            res.statusCode = 500;
-            return res.json({error: {
-                message: 'A nova senha é a mesma da senha atual'
-            }});
+          res.statusCode = 500;
+          return res.json({
+            error: {
+              message: 'A senha atual está incorreta'
+            }
+          });
         }
+      } else {
+        res.statusCode = 500;
+        return res.json({
+          error: {
+            message: 'A nova senha é a mesma da senha atual'
+          }
+        });
+      }
     }
 
-    user.save(err => {
-      if(err) {
+    user.save().catch(err => {
+      if (err) {
         log.error(err);
-        return next({message: 'Erro ao atualizar usuário'});
+        return next({ message: 'Erro ao atualizar usuário' });
       }
       let u = user.toObject();
       delete u.hashedPassword;
       delete u.salt;
-      res.json({user: u});
+      res.json({ user: u });
     })
+  }).catch(function (err) {
+    if (err) {
+      log.error(err);
+      return next({ err });
+    }
   })
 });
 
-userApp.get('/reset/password', (req, res, next) => {
+
+userApp.get('/reset/password', async (req, res, next) => {
   let emailAddress = req.query.email;
-  User.findOne({where:{email: emailAddress}}, (err, user)=> {
-    if(err) {
-      log.error(err);
-      let errors = [];
-      for(let errName in err.errors) {
-        errors.push(err.errors[errName].message);
-      }
-      res.statusCode = 400;
-      return res.json({err, errors});
+  let user = await User.findOne({ where: { email: emailAddress } }).catch(function (err) {
+    log.error(err);
+    let errors = [];
+    for (let errName in err.errors) {
+      errors.push(err.errors[errName].message);
     }
-    if (!user) {
+    log.error(errors);
+    res.statusCode = 400;
+    return res.json({ err, errors });
+    // handle error;
+  });
+  if (!user) {
+    res.statusCode = 404;
+    res.json({ msg: "O usuário não está cadastrado" });
+  }
+  else {
+    let tokenValue = uuid.v4();
+    const rt = await ResetToken.create({
+      user_id: user.id,
+      token: tokenValue,
+      reset: false
+    });
+    if (!rt) {
       res.statusCode = 404;
-      res.json({msg: "O usuário não está cadastrado"});
+      return res.json({ msg: "Couldn't create Reset Password Token" });
     }
-    else {
-      let resetToken = new ResetToken({
-        userId: user._id
-      });
-      resetToken.createResetToken((err, token) => {
-        if (err) {
-          log.error(err);
-          return next(err);
-        }
-        let url = config.default.lde.url + '/reset-password';
-        let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${token}`;
-        let mailOptions = {
-          to: `"${user.name} <${user.email}>"`,
-          subject: "Redefinição de Senha - Laboratório de Dados Educacionais",
-          text
-        }
-        email(mailOptions, (err, info) => {
-          if(err) {
-            log.error(err);
-            res.json({msg: 'Undelivered Reset Password Mail'});
-          }
-          log.info(`Message ${info.messageId} sent: ${info.response}`);
-          res.json({msg: 'Reset Password Mail Successfully Delivered'});
-        });
-      })
+    let url = config.default.lde.url + '/reset-password';
+    let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${tokenValue}`;
+    let mailOptions = {
+      to: `"${user.name} <${user.email}>"`,
+      subject: "Redefinição de Senha - Laboratório de Dados Educacionais",
+      text
     }
-  }).then((result) => res.json(result));
-})
+    console.log(mailOptions);
+    email(mailOptions, (err, info) => {
+      if (err) {
+        console.log(err);
+        log.error(err);
+        res.json({ msg: 'Undelivered Reset Password Mail' });
+      }
+      log.info(`Message ${info.messageId} sent: ${info.response}`);
+      res.json({ msg: 'Reset Password Mail Successfully Delivered' });
+    });
+  }
+});
 
 module.exports = userApp;
-- 
GitLab


From dff177b1fdb057528e1cfcabb30472e658531cb9 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 10 May 2023 11:41:53 -0300
Subject: [PATCH 018/123] [FIX] User put /:id

---
 src/libs/middlewares/passport.js  |  79 +++++++++----------
 src/libs/models/user.js           |  22 ++----
 src/libs/routes_v1/user.js        | 125 +++++++++++++++---------------
 src/libs/routes_v1/verifyToken.js |  54 ++++++-------
 4 files changed, 135 insertions(+), 145 deletions(-)

diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js
index 93f5fe0a..82f21de5 100644
--- a/src/libs/middlewares/passport.js
+++ b/src/libs/middlewares/passport.js
@@ -11,51 +11,48 @@ const AccessToken = require(`${libs}/models/accessToken`);
 
 
 passport.use(new ClientPasswordStrategy(
-    function(client_id, client_secret, done) {
-      Client.findOne({where: {id: client_id} 
-      }).then(function(client) {
-        if(!client){
-            console.log("Erro de cliente");
-            return done(null, false);
-        }
-        if (client.client_secret !== client_secret){
-            console.log("Erro de geracao Chave Secreta");
-            return done(null, false);
-        } 
-        return done(null, client);
-      }).catch(function(error) {
-        return done(error);
-      });
-    }
-  ));
-
-passport.use(new BearerStrategy( (accessToken, done) => {
-        console.log("Entrei no BearerStrategy");
-        AccessToken.findOne({where:{token: accessToken}}).then((token) => {
-            if (!token) {
-                console.log("ERRO Token");
+    function (client_id, client_secret, done) {
+        Client.findOne({
+            where: { id: client_id }
+        }).then(function (client) {
+            if (!client) {
+                console.log("Erro de cliente");
+                return done(null, false);
+            }
+            if (client.client_secret !== client_secret) {
+                console.log("Erro de geracao Chave Secreta");
                 return done(null, false);
             }
+            return done(null, client);
+        }).catch(function (error) {
+            return done(error);
+        });
+    }
+));
 
-            if( Math.round((Date.now()-token.created)/1000) > config.security.tokenLife) {
-                AccessToken.destroy({ token: accessToken }).then((err) => {
-                    if (err) {
-                        console.log("ERRO remove Token")
-                        return done(err);
-                    }
-                });
+passport.use(new BearerStrategy(async (accessToken, done) => {
+    const token = await AccessToken.findOne({ where: { token: accessToken } })
+    if (!token) {
+        console.log("ERRO Token");
+        return done(null, false);
+    }
 
-                return done(null, false, { msg: 'Token expired' });
+    if (Math.round((Date.now() - token.created) / 1000) > config.security.tokenLife) {
+        AccessToken.destroy({ token: accessToken }).then((err) => {
+            if (err) {
+                console.log("ERRO remove Token")
+                return done(err);
             }
+        });
 
-            User.findByPk(token.user_id).then(function(usuario) {
-                if (!usuario) {
-                    console.log("ERRO NAO USUARIO");
-                    return done(null, false, { msg: 'Unknown user' });
-                }
-                var info = { scope: '*' };
-                done(null, usuario, info);
-            })
-        })
+        return done(null, false, { msg: 'Token expired' });
     }
-));
+    User.findByPk(token.user_id).then(function (usuario) {
+        if (!usuario) {
+            console.log("ERRO NAO USUARIO");
+            return done(null, false, { msg: 'Unknown user' });
+        }
+        var info = { scope: '*' };
+        done(null, usuario, info);
+    })
+}));
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 25fd8abb..5da038dd 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -20,27 +20,18 @@ var User = db.define("User",{
         }
     },
     password:{
-        type: Sequelize.VIRTUAL,
-        get(){
-            return () => this.getDataValue('password')
-        }
+        type: Sequelize.VIRTUAL
     },
     hashed_password:{
         type: Sequelize.STRING,
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Senha é obrigatório." },
-        },
-        get() {
-            return() => this.getDataValue('hashed_password')
         }
     },
     salt: {
         type: Sequelize.STRING,
         allowNull: false,
-        get() {
-            return() => this.getDataValue('salt')
-        }
     },
     name:{
         type: Sequelize.STRING,
@@ -165,8 +156,8 @@ User.generateSalt = function() {
     return crypto.randomBytes(128).toString('hex');
 }
 
-User.encryptPassword = function(password) {
-    return crypto.pbkdf2Sync(password+'', this.salt, 10000, 512, 'sha512');
+User.encryptPassword = function(password, salt) {
+    return crypto.pbkdf2Sync(password+'', salt, 10000, 512, 'sha512').toString('hex');
 }
 
 User.generateObjectId = function(){
@@ -179,8 +170,7 @@ User.generateObjectId = function(){
 const setSaltAndPassword = user => {
     if (user.changed('password')) {
         user.salt = User.generateSalt()
-        user.password = User.encryptPassword(user.password())
-        this.hashed_password = this.encryptPassword(password).toString('hex');
+        user.hashed_password = User.encryptPassword(user.password, user.salt).toString('hex');
     }
 }
 
@@ -192,8 +182,8 @@ User.beforeCreate(setSaltAndPassword)
 User.beforeCreate(setObjectId)
 User.beforeUpdate(setSaltAndPassword)
 
-User.prototype.checkPassword = function(enteredPassword) {
-    return User.encryptPassword(enteredPassword, this.salt()) === this.hashed_password()
+User.prototype.checkPassword = function(user, enteredPassword) {
+    return User.encryptPassword(enteredPassword, user.salt) === user.hashed_password
 }
 
 module.exports = User; 
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index ebe3191a..d53d2748 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -94,11 +94,10 @@ userApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, n
 */
 
 userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, res, next) => {
-  res.json(req.user);
-  let user = req.user.toObject();
-  delete user.hashedPassword;
-  delete user.salt;
-  req.result = user;
+  let u = req.user;
+  delete u.hashedPassword;
+  delete u.salt;
+  req.result = u;
   next();
 }, response('user'));
 
@@ -108,16 +107,17 @@ userApp.get('/:id', (req, res, next) => {
       res.statusCode = 404;
       res.json({ msg: "O usuário não está cadastrado" });
     } else {
-      let u = user.toObject;
+      let u = user;
       delete u.hashedPassword;
       delete u.salt;
       req.result = u;
+      next();
     }
   }).catch(function (err) {
     log.error(err);
     return next(err);
   });
-});
+}, response('user'));
 
 userApp.post('/', async (req, res, next) => {
   if (typeof req.body.password === 'undefined' || !req.body.password) {
@@ -189,73 +189,76 @@ userApp.post('/', async (req, res, next) => {
   }
 });
 
-userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
-  User.findByPk(req.params.id).then((user) => {
-    if (!user) {
-      res.statusCode = 404;
-      return next({
-        err: {
-          message: 'Usuário não encontrado'
-        }
-      });
+userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  let user = await User.findByPk(req.params.id).catch(function (err) {
+    if (err) {
+      log.error(err);
+      return next({ err });
     }
+  })
+  if (!user) {
+    res.statusCode = 404;
+    return next({
+      err: {
+        message: 'Usuário não encontrado'
+      }
+    });
+  }
 
-    user.email = req.body.email || user.email;
-    user.name = req.body.name || user.name;
-    user.nickname = req.body.nickname || user.nickname || user.name;
-    user.cep = req.body.cep || user.cep;
-    user.complement = req.body.complement || user.complement;
-    user.address = req.body.address || user.address;
-    user.phone = req.body.phone || user.phone;
-    user.schooling = req.body.schooling || user.schooling;
-    user.course = req.body.course || user.course;
-    user.segment = req.body.segment || user.segment;
-    user.role = req.body.role || user.role;
-    user.institutionName = req.body.institutionName || user.institutionName;
-    user.state = req.body.state || user.state;
-    user.city = req.body.city || user.city;
-    user.receiveEmails = req.body.receiveEmails || user.receiveEmails;
-    user.citesegment = req.body.citesegment || user.citesegment;
-    user.citerole = req.body.citerole || user.citerole;
+  user.email = req.body.email || user.email;
+  user.name = req.body.name || user.name;
+  user.nickname = req.body.nickname || user.nickname || user.name;
+  user.cep = req.body.cep || user.cep;
+  user.complement = req.body.complement || user.complement;
+  user.address = req.body.address || user.address;
+  user.phone = req.body.phone || user.phone;
+  user.schooling = req.body.schooling || user.schooling;
+  user.course = req.body.course || user.course;
+  user.segment = req.body.segment || user.segment;
+  user.role = req.body.role || user.role;
+  user.institutionName = req.body.institutionName || user.institutionName;
+  user.state = req.body.state || user.state;
+  user.city = req.body.city || user.city;
+  user.receiveEmails = req.body.receiveEmails || user.receiveEmails;
+  user.citesegment = req.body.citesegment || user.citesegment;
+  user.citerole = req.body.citerole || user.citerole;
 
-    if ((req.body.password) && (req.body.newpassword)) {
-      if (req.body.password != req.body.newpassword) {
-        if (user.checkPassword(req.body.password)) {
-          user.password = req.body.newpassword;
-        } else {
-          res.statusCode = 500;
-          return res.json({
-            error: {
-              message: 'A senha atual está incorreta'
-            }
-          });
-        }
-      } else {
+
+
+  if ((req.body.password) && (req.body.newpassword)) {
+    if (req.body.password != req.body.newpassword) {
+      if (user.checkPassword(user, req.body.password)) {
+          await user.update({password:req.body.newpassword});
+        } 
+      else {
         res.statusCode = 500;
         return res.json({
           error: {
-            message: 'A nova senha é a mesma da senha atual'
+            message: 'A senha atual está incorreta'
           }
         });
       }
+    } else {
+      res.statusCode = 500;
+      return res.json({
+        error: {
+          message: 'A nova senha é a mesma da senha atual'
+        }
+      });
     }
+  }
 
-    user.save().catch(err => {
-      if (err) {
-        log.error(err);
-        return next({ message: 'Erro ao atualizar usuário' });
-      }
-      let u = user.toObject();
-      delete u.hashedPassword;
-      delete u.salt;
-      res.json({ user: u });
-    })
-  }).catch(function (err) {
+  user.save().catch(err => {
     if (err) {
       log.error(err);
-      return next({ err });
-    }
-  })
+      return next({ message: 'Erro ao atualizar usuário' });
+    }})
+  let u = user;
+  delete u.hashedPassword;
+  delete u.salt;
+  delete u.password;
+  res.json({ user: u });
+
 });
 
 
diff --git a/src/libs/routes_v1/verifyToken.js b/src/libs/routes_v1/verifyToken.js
index 8cc9e35d..e69e4761 100644
--- a/src/libs/routes_v1/verifyToken.js
+++ b/src/libs/routes_v1/verifyToken.js
@@ -12,40 +12,40 @@ const User = require(`${libs}/models/user`);
 
 verifyTokenApp.get('/:token', (req, res, next) => {
     let token = req.params.token;
-    VerificationToken.findOne({token: token}, (err, vToken) => {
-        if(err) {
+    let vToken = VerificationToken.findOne({ where: { token: token } }).catch(function (err) {
+        if (err) {
             log.error(err);
-            return next(err);
+            return next({ err });
         }
-        if(!vToken) {
-            // TODO: generate new verification token
-            res.statusCode = 404;
-            return next({msg: 'Token not found', status:404});
+    })
+    if (!vToken) {
+        // TODO: generate new verification token
+        res.statusCode = 404;
+        return next({ msg: 'Token not found', status: 404 });
+    }
+    User.findByPk(vToken.userId, (user) => {
+        if (err) {
+            log.error(err);
+            next(err);
         }
-        User.findByPk(vToken.userId, (err, user) => {
-            if(err) {
+        user.verified = true;
+        user.save((err) => {
+            if (err) {
+                log.error(err);
+                next(err);
+            }
+        });
+        let u = user.toObject();
+        delete u.salt;
+        delete u.hashedPassword;
+        vToken.verified = true;
+        vToken.save((err) => {
+            if (err) {
                 log.error(err);
                 next(err);
             }
-            user.verified = true;
-            user.save((err) => {
-                if(err) {
-                    log.error(err);
-                    next(err);
-                }
-            });
-            let u = user.toObject();
-            delete u.salt;
-            delete u.hashedPassword;
-            vToken.verified = true;
-            vToken.save((err) => {
-                if(err) {
-                    log.error(err);
-                    next(err);
-                }
-            });
-            res.json({msg: 'User verified', user: u});
         });
+        res.json({ msg: 'User verified', user: u });
     });
 });
 
-- 
GitLab


From 669005f86c5ad9ba2407114cc817400cb53aba51 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Fri, 12 May 2023 10:24:53 -0300
Subject: [PATCH 019/123] FIX user POST

---
 src/libs/models/user.js           | 11 ++++++-----
 src/libs/routes_v1/user.js        |  5 ++++-
 src/libs/routes_v1/verifyToken.js |  2 +-
 3 files changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 5da038dd..3f0421b8 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -139,7 +139,7 @@ var User = db.define("User",{
         defaultValue:false
     },
     citesegment:{
-        type: Sequelize.STRING
+        type: Sequelize.STRING,
     },
     citerole:{
         type: Sequelize.STRING
@@ -168,13 +168,14 @@ User.generateObjectId = function(){
 }
 
 const setSaltAndPassword = user => {
-    if (user.changed('password')) {
-        user.salt = User.generateSalt()
-        user.hashed_password = User.encryptPassword(user.password, user.salt).toString('hex');
-    }
+    // if (user.changed('password')) {
+    user.salt = User.generateSalt()
+    user.hashed_password = User.encryptPassword(user.password, user.salt).toString('hex');
+   // }
 }
 
 const setObjectId = user => {
+   console.log("I'm in")
    user.id = User.generateObjectId()
 };
 
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index d53d2748..cc45d7ff 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -125,8 +125,11 @@ userApp.post('/', async (req, res, next) => {
     return res.json({ errors: ["O campo senha é obrigatório"] });
   } else {
     let user = await User.create({
+      id: 0,
       email: req.body.email,
       password: req.body.password,
+      hashed_password: 0,
+      salt: 0,
       name: req.body.name,
       nickname: req.body.nickname,
       cpf: req.body.cpf,
@@ -138,7 +141,7 @@ userApp.post('/', async (req, res, next) => {
       course: req.body.course,
       segment: req.body.segment,
       role: req.body.role,
-      institutionName: req.body.institutionName,
+      institution_name: req.body.institutionName,
       state: req.body.state,
       city: req.body.city,
       receiveEmails: false || req.body.receiveEmails,
diff --git a/src/libs/routes_v1/verifyToken.js b/src/libs/routes_v1/verifyToken.js
index e69e4761..5cef8311 100644
--- a/src/libs/routes_v1/verifyToken.js
+++ b/src/libs/routes_v1/verifyToken.js
@@ -35,7 +35,7 @@ verifyTokenApp.get('/:token', (req, res, next) => {
                 next(err);
             }
         });
-        let u = user.toObject();
+        let u = user;
         delete u.salt;
         delete u.hashedPassword;
         vToken.verified = true;
-- 
GitLab


From bc9ed9d89dd4d08ce31b4181c0b65377479f8cd7 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Fri, 12 May 2023 11:50:11 -0300
Subject: [PATCH 020/123] verifyToken adapted

---
 src/libs/api_mongo.txt            |  2 +-
 src/libs/routes_v1/user.js        |  4 +--
 src/libs/routes_v1/verifyToken.js | 44 ++++++++++++++++---------------
 3 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/src/libs/api_mongo.txt b/src/libs/api_mongo.txt
index c1353348..d43ea00a 100644
--- a/src/libs/api_mongo.txt
+++ b/src/libs/api_mongo.txt
@@ -1,5 +1,5 @@
 *downloads.js
 *resetToken.js
 *simulation.js
-*user.js
+*user.js (V)
 *verifyToken.js
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index cc45d7ff..53f3ef54 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -95,7 +95,7 @@ userApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, n
 
 userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, res, next) => {
   let u = req.user;
-  delete u.hashedPassword;
+  delete u.hashed_password;
   delete u.salt;
   req.result = u;
   next();
@@ -108,7 +108,7 @@ userApp.get('/:id', (req, res, next) => {
       res.json({ msg: "O usuário não está cadastrado" });
     } else {
       let u = user;
-      delete u.hashedPassword;
+      delete u.hashed_password;
       delete u.salt;
       req.result = u;
       next();
diff --git a/src/libs/routes_v1/verifyToken.js b/src/libs/routes_v1/verifyToken.js
index 5cef8311..d1c75b5f 100644
--- a/src/libs/routes_v1/verifyToken.js
+++ b/src/libs/routes_v1/verifyToken.js
@@ -10,9 +10,9 @@ const VerificationToken = require(`${libs}/models/verificationToken`);
 
 const User = require(`${libs}/models/user`);
 
-verifyTokenApp.get('/:token', (req, res, next) => {
+verifyTokenApp.get('/:token', async (req, res, next) => {
     let token = req.params.token;
-    let vToken = VerificationToken.findOne({ where: { token: token } }).catch(function (err) {
+    let vToken = await VerificationToken.findOne({ where: { token: token } }).catch(function (err) {
         if (err) {
             log.error(err);
             return next({ err });
@@ -23,30 +23,32 @@ verifyTokenApp.get('/:token', (req, res, next) => {
         res.statusCode = 404;
         return next({ msg: 'Token not found', status: 404 });
     }
-    User.findByPk(vToken.userId, (user) => {
+    let _user = await User.findByPk(vToken.user_id).catch(function (err) {
+        if (err) {
+            log.error(err);
+            return next({ err });
+        }
+    }) 
+    if(!_user){
+        return done(null, false); 
+    }
+    await _user.update({verified:true});
+    await _user.save().catch(err => {
+        if (err) {
+          log.error(err);
+          return next({ message: 'Erro ao atualizar usuário' });
+        }})
+    vToken.verified = true;
+    await vToken.save().catch(err => {
         if (err) {
             log.error(err);
             next(err);
         }
-        user.verified = true;
-        user.save((err) => {
-            if (err) {
-                log.error(err);
-                next(err);
-            }
-        });
-        let u = user;
-        delete u.salt;
-        delete u.hashedPassword;
-        vToken.verified = true;
-        vToken.save((err) => {
-            if (err) {
-                log.error(err);
-                next(err);
-            }
-        });
-        res.json({ msg: 'User verified', user: u });
     });
+    let u = _user;
+    delete u['salt'];
+    delete u['hashed_password'];
+    res.json({ msg: 'User verified', u });
 });
 
 module.exports = verifyTokenApp;
-- 
GitLab


From d8f7962a458b4a30ae8ea5c4234aa43781f42105 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 15 May 2023 11:57:36 -0300
Subject: [PATCH 021/123] [ADD] ResetToken Postgres methods

---
 src/libs/api_mongo.txt            |   5 +-
 src/libs/middlewares/passport.js  |   6 +-
 src/libs/models/resetToken.js     |   2 +-
 src/libs/routes_v1/resetToken.js  | 114 +++++++++++++++---------------
 src/libs/routes_v1/user.js        |   8 +--
 src/libs/routes_v1/verifyToken.js |   2 +-
 6 files changed, 69 insertions(+), 68 deletions(-)

diff --git a/src/libs/api_mongo.txt b/src/libs/api_mongo.txt
index d43ea00a..58f65965 100644
--- a/src/libs/api_mongo.txt
+++ b/src/libs/api_mongo.txt
@@ -1,5 +1,6 @@
 *downloads.js
-*resetToken.js
+*resetToken.js (V)
+*pqr.js
 *simulation.js
 *user.js (V)
-*verifyToken.js
+*verifyToken.js (V)
diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js
index 82f21de5..a63cd150 100644
--- a/src/libs/middlewares/passport.js
+++ b/src/libs/middlewares/passport.js
@@ -38,10 +38,10 @@ passport.use(new BearerStrategy(async (accessToken, done) => {
     }
 
     if (Math.round((Date.now() - token.created) / 1000) > config.security.tokenLife) {
-        AccessToken.destroy({ token: accessToken }).then((err) => {
+        AccessToken.destroy({where:{ token: accessToken} }).catch(function (err) {
             if (err) {
-                console.log("ERRO remove Token")
-                return done(err);
+                log.error(err);
+                return next({ err });
             }
         });
 
diff --git a/src/libs/models/resetToken.js b/src/libs/models/resetToken.js
index 5398e9f4..21e5915e 100644
--- a/src/libs/models/resetToken.js
+++ b/src/libs/models/resetToken.js
@@ -27,7 +27,7 @@ var ResetToken = db.define("ResetToken",{
     {timestamps: false}
 );
 
-ResetToken.hasExpired = function () {
+ResetToken.prototype.hasExpired = function () {
     var now = new Date();
     return (now - this.createdAt) > 86400; //Expire if token is 1 day old
 };
diff --git a/src/libs/routes_v1/resetToken.js b/src/libs/routes_v1/resetToken.js
index 3d67a1e2..7c758410 100644
--- a/src/libs/routes_v1/resetToken.js
+++ b/src/libs/routes_v1/resetToken.js
@@ -10,72 +10,72 @@ const ResetToken = require(`${libs}/models/resetToken`);
 
 const User = require(`${libs}/models/user`);
 
-resetTokenApp.get('/:token', (req, res, next) => {
+resetTokenApp.get('/:token', async (req, res, next) => {
     let token = req.params.token;
-    ResetToken.findOne({token: token}, (err, rToken) => {
-        if(err) {
+    let rToken = await ResetToken.findOne({where:{ token: token} }).catch(function (err) {
+        if (err) {
             log.error(err);
-            return next(err);
+            return next({ err });
         }
-        if(!rToken) {
-            // TODO: generate new reset token
-            res.statusCode = 404;
-            return next({msg: 'Token not found', status:404});
-        }
-        if (rToken.hasExpired()) {
-            res.statusCode = 410;
-            ResetToken.remove({token: token}, (err) => {
-                if(err) {
-                    log.error(err);
-                    next(err);
-                }
-            })
-            return next({msg: 'Token expired', status: 410});
-        }
-        User.findByPk(rToken.userId, (err, user) => {
-            if(err) {
+    })
+    if (!rToken) {
+        res.statusCode = 404;
+        return next({ msg: 'Token not found', status: 404 });
+    }
+    if (rToken.hasExpired()) {
+        res.statusCode = 410;
+        await ResetToken.remove({where:{ token: token}}).catch(function (err) {
+            if (err) {
                 log.error(err);
-                next(err);
+                return next({ err });
             }
-            let u = user.toObject();
-            delete u.salt;
-            delete u.hashedPassword;
-            res.json({user: u});
-        });
-    });
+        })
+        return next({ msg: 'Token expired', status: 410 });
+    }
+    let _user = await User.findByPk(rToken.user_id).catch(function (err) {
+        if (err) {
+            log.error(err);
+            return next({ err });
+        }
+    })
+    let u = _user.toJSON();
+    delete u.salt;
+    delete u.hashed_password;
+    res.json({ user: u });
 });
-resetTokenApp.post('/:token', (req, res, next) => {
+
+resetTokenApp.post('/:token', async (req, res, next) => {
     let token = req.params.token;
-    ResetToken.findOne({token: token}, (err, rToken) => {
-        if(err) {
+    let rToken = await ResetToken.findOne({where:{ token: token}}).catch(function (err) {
+        if (err) {
             log.error(err);
-            return next(err);
+            return next({ err });
         }
-        if(!rToken) {
-            res.statusCode = 404;
-            return next({msg: 'Token not found', status:404});
+    })
+    if (!rToken) {
+        res.statusCode = 404;
+        return next({ msg: 'Token not found', status: 404 });
+    }
+    let _user = await User.findByPk(rToken.user_id).catch(function (err) {
+        if (err) {
+            log.error(err);
+            return next({ err });
         }
-        User.findByPk(rToken.userId, (err, user) => {
-            if(err) {
-                log.error(err);
-                next(err);
-            }
-            user.password = req.body.password;
-            user.save((err) => {
-                if(err) {
-                    log.error(err);
-                    next(err);
-                }
-                ResetToken.remove({token: token}, (err) => {
-                    if(err) {
-                        log.error(err);
-                        next(err);
-                    }
-                })
-                res.json({msg: "Senha alterada com sucesso"});
-            })
-        });
-    });
-})
+    })
+    await _user.update({password:req.body.password});
+    _user.save().catch(function (err) {
+        if (err) {
+            log.error(err);
+            return next({ err });
+        }
+    })
+    await ResetToken.destroy({where:{token: token} }).catch(function (err) {
+        if (err) {
+            log.error(err);
+            return next({ err });
+        }
+    })
+    res.json({ msg: "Senha alterada com sucesso" });
+});
 
 module.exports = resetTokenApp;
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 53f3ef54..d02edd84 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -94,7 +94,7 @@ userApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, n
 */
 
 userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, res, next) => {
-  let u = req.user;
+  let u = req.user.toJSON();
   delete u.hashed_password;
   delete u.salt;
   req.result = u;
@@ -107,7 +107,7 @@ userApp.get('/:id', (req, res, next) => {
       res.statusCode = 404;
       res.json({ msg: "O usuário não está cadastrado" });
     } else {
-      let u = user;
+      let u = user.toJSON();
       delete u.hashed_password;
       delete u.salt;
       req.result = u;
@@ -256,8 +256,8 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (
       log.error(err);
       return next({ message: 'Erro ao atualizar usuário' });
     }})
-  let u = user;
-  delete u.hashedPassword;
+  let u = user.toJSON();
+  delete u.hashed_password;
   delete u.salt;
   delete u.password;
   res.json({ user: u });
diff --git a/src/libs/routes_v1/verifyToken.js b/src/libs/routes_v1/verifyToken.js
index d1c75b5f..81731fe3 100644
--- a/src/libs/routes_v1/verifyToken.js
+++ b/src/libs/routes_v1/verifyToken.js
@@ -45,7 +45,7 @@ verifyTokenApp.get('/:token', async (req, res, next) => {
             next(err);
         }
     });
-    let u = _user;
+    let u = _user.toJSON();
     delete u['salt'];
     delete u['hashed_password'];
     res.json({ msg: 'User verified', u });
-- 
GitLab


From 7a8281ea03f064362ffe2746d52907e47ee6f1eb Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 17 May 2023 11:33:26 -0300
Subject: [PATCH 022/123] [ADD] Simulation and pqr

---
 src/libs/models/pqr.js           |  27 +++---
 src/libs/models/simulation.js    |  51 ++++++-----
 src/libs/models/user.js          |   1 -
 src/libs/routes_v1/simulation.js | 153 ++++++++++++++-----------------
 4 files changed, 113 insertions(+), 119 deletions(-)

diff --git a/src/libs/models/pqr.js b/src/libs/models/pqr.js
index f9270318..2a9fb9dd 100644
--- a/src/libs/models/pqr.js
+++ b/src/libs/models/pqr.js
@@ -1,16 +1,19 @@
-const mongoose = require('mongoose')
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
 
-const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
-const User = require(`${libs}/models/user`);
-
-const Schema = mongoose.Schema;
-
-let PQRSchema = new Schema({
+var PQR = db.define("pqr",{
+    id:{
+        type: Sequelize.INTEGER, 
+        allowNull: false,
+        unique: true,
+        primaryKey: true
+    },
     content: {
-        type: String,
-        required: true,
+        type: Sequelize.STRING, 
+        allowNull: false
     }
-});
+},
+{timestamps: false});
+
 
-module.exports = mongoose.model('PQR', PQRSchema);
+module.exports = PQR;
diff --git a/src/libs/models/simulation.js b/src/libs/models/simulation.js
index f1d3b0bd..21d2de89 100644
--- a/src/libs/models/simulation.js
+++ b/src/libs/models/simulation.js
@@ -1,34 +1,39 @@
-const mongoose = require('mongoose')
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js')
+const User = require(`./user.js`);
 
-const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
-const User = require(`${libs}/models/user`);
-
-const Schema = mongoose.Schema;
-
-let SimulationSchema = new Schema({
-    userId: {
-        type: Schema.Types.ObjectId,
-        required: true,
-        ref: 'User'
+var Simulation = db.define("Simulation",{
+    id:{
+        type: Sequelize.INTEGER, 
+        allowNull: false,
+        autoIncrement: true,
+        unique: true,
+        primaryKey: true
     },
-    content: {
-        type: String,
-        required: true,
+    user_id: {
+        type: Sequelize.STRING, 
+        allowNull: false
+    },
+    content:{
+        type: Sequelize.STRING,
+        allowNull: false
     },
     name: {
-        type: String
+        type: Sequelize.STRING,
     },
-    createdAt: {
-        type: Date,
-        required: true,
-        default: Date.now
+    created_at:{
+        type: Sequelize.DATE,
+        allowNull: false,
+        defaultValue: Date.now
     },
-    updatedAt: {
+    updated_at: {
         type: Date,
         required: true,
         default: Date.now
     }
-});
+},
+{timestamps: false});
+
+Simulation.hasOne(User,{ foreignKey: 'id' });
 
-module.exports = mongoose.model('Simulation', SimulationSchema);
+module.exports = Simulation;
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 3f0421b8..a1ab8e27 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -175,7 +175,6 @@ const setSaltAndPassword = user => {
 }
 
 const setObjectId = user => {
-   console.log("I'm in")
    user.id = User.generateObjectId()
 };
 
diff --git a/src/libs/routes_v1/simulation.js b/src/libs/routes_v1/simulation.js
index 4b2e40c3..ea9921d3 100644
--- a/src/libs/routes_v1/simulation.js
+++ b/src/libs/routes_v1/simulation.js
@@ -6,12 +6,6 @@ const libs = `${process.cwd()}/libs`;
 
 const log = require(`${libs}/log`)(module);
 
-const squel = require('squel');
-
-const query = require(`${libs}/middlewares/query`).query;
-
-const response = require(`${libs}/middlewares/response`);
-
 const Simulation = require(`${libs}/models/simulation`);
 
 const PQR = require(`${libs}/models/pqr`);
@@ -20,7 +14,7 @@ const passport = require('passport');
 
 simulationApp.get('/time', (req, res, next) => {
     const maxTime = parseInt(req.query.max_time, 10);
-    if(isNaN(maxTime)) {
+    if (isNaN(maxTime)) {
         res.status(400);
         next({
             status: 400,
@@ -28,57 +22,54 @@ simulationApp.get('/time', (req, res, next) => {
         });
     }
     res.json({
-        result: Array.apply(null, {length: maxTime}).map(Number.call, Number).map((i)=>i+1)
+        result: Array.apply(null, { length: maxTime }).map(Number.call, Number).map((i) => i + 1)
     });
 });
 
-simulationApp.get('/pqr', (req, res) => {
-    PQR.findOne((err, pqr) => {
-        if(err) {
+simulationApp.get('/pqr', async (req, res) => {
+    let pqr = await PQR.findOne({ attributes: ['content'] }).catch(function (err) {
+        if (err) {
             log.error(err);
-            return next({err});
+            return next({ err });
         }
-
-        res.json(pqr);
     });
+    if (pqr)
+        res.json(pqr);
 });
 
-simulationApp.put('/pqr', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+simulationApp.put('/pqr', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
     let user = req.user.toObject();
-
-    PQR.findOne((err, pqr) => {
-        if(err) {
-            log.error(err)
-            return next({err});
+    let pqr = await PQR.findOne({ attributes: ['content'] }).catch(function (err) {
+        if (err) {
+            log.error(err);
+            return next({ err });
         }
-
-        if(!user.admin) {
-            log.info(`Usuário ${user.email} tentou alterar o PQR, mas não tem privilégio`);
-            res.statusCode = 401;
-            return next({err: { msg: 'Unauthorized'}});
+    });
+    if (!user.admin) {
+        log.info(`Usuário ${user.email} tentou alterar o PQR, mas não tem privilégio`);
+        res.statusCode = 401;
+        return next({ err: { msg: 'Unauthorized' } });
+    }
+    pqr.content = req.body.content || pqr.content;
+    pqr.save().catch(function (err) {
+        if (err) {
+            log.error(err);
+            return next({ err });
         }
-        pqr.content = req.body.content || pqr.content;
-        pqr.save((err) => {
-            if(err) {
-                log.error(err);
-                return next({err});
-            }
-            res.json({msg: 'PQR updated'})
-        });
     });
+    res.json({ msg: 'PQR updated' })
+
 });
 
-simulationApp.get('/', passport.authenticate('bearer', { session: false }), (req, res) => {
+simulationApp.get('/', passport.authenticate('bearer', { session: false }), async (req, res) => {
     let user = req.user.toObject();
-    let query = Simulation.find({userId: user._id}).select('userId name createdAt updatedAt');
-    query.exec((err, simulations) => {
-        if(err) {
+    let simulations = await Simulation.findAll({ where: { user_id: user.id } }, { attributes: ['user_id', 'name', 'created_at', 'updated_at'] }).catch(function (err) {
+        if (err) {
             log.error(err);
-            return next({err});
+            return next({ err });
         }
-
-        res.json(simulations);
-    });
+    })
+    res.json(simulations);
 
     // Simulation.find({userId: user._id}, (err, simulations) => {
     //     if(err) {
@@ -90,78 +81,74 @@ simulationApp.get('/', passport.authenticate('bearer', { session: false }), (req
     // });
 });
 
-simulationApp.post('/', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+simulationApp.post('/', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
     let user = req.user.toObject();
 
-    let simulation = new Simulation({
-        userId: user._id,
+    let simulation = await Simulation.create({
+        user_id: user.id,
         content: req.body.content,
         name: req.body.name
     });
 
-    simulation.save((err) => {
-        if(err) {
+    await simulation.save().catch(function (err) {
+        if (err) {
             log.error(err);
-            return next({err});
+            return next({ err });
         }
+    });
 
-        res.json({msg: 'Simulation created', simulation});
-    })
-});
+    res.json({ msg: 'Simulation created', simulation });
+})
 
-simulationApp.get('/:id', passport.authenticate('bearer', { session: false }), (req, res) => {
+simulationApp.get('/:id', passport.authenticate('bearer', { session: false }), async (req, res) => {
     let user = req.user.toObject();
 
-    Simulation.findOne({_id: req.params.id, userId: user._id}, (err, simulation) => {
-        if(err) {
+    let simulation = await Simulation.findOne({where:{id: req.params.id, userId: user._id }}).catch(function (err) {
+        if (err) {
             log.error(err);
-            return next({err});
+            return next({ err });
         }
-
-        res.json(simulation);
     });
+    res.json(simulation);
+
 });
 
-simulationApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+simulationApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
     let user = req.user.toObject();
 
-    Simulation.findOne({_id: req.params.id, userId: user._id}, (err, simulation) => {
-        if(err) {
+    let simulation = await Simulation.findOne({ where: { id: req.params.id, user_id: user.id } }).catch(function (err) {
+        if (err) {
             log.error(err);
-            return next({err});
-        }
-
-        if(!simulation) {
-            res.statusCode = 404;
-            return next({err: { msg: 'Simulation not found'}});
+            return next({ err });
         }
+    });
 
-        simulation.content = req.body.content || simulation.content;
-        simulation.name = req.body.name || simulation.name;
-        simulation.updatedAt = Date.now();
-
-        simulation.save((err) => {
-            if(err) {
-                log.error(err);
-                return next(err);
-            }
+    if (!simulation) {
+        res.statusCode = 404;
+        return next({ err: { msg: 'Simulation not found' } });
+    }
 
-            res.json(simulation);
-        });
+    simulation.content = req.body.content || simulation.content;
+    simulation.name = req.body.name || simulation.name;
+    simulation.updated_at = Date.now();
+    simulation.save().catch(function (err) {
+        if (err) {
+            log.error(err);
+            return next({ err });
+        }
     });
+    res.json(simulation);
 });
 
-simulationApp.delete('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+simulationApp.delete('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
     let user = req.user.toObject();
-
-    Simulation.remove({_id: req.params.id, userId: user._id}, (err, simulation) => {
-        if(err) {
+    await Simulation.destroy({ where: { "id": req.params.id, "user_id": user._id } }).catch(function (err) {
+        if (err) {
             log.error(err);
-            return next({err});
+            return next({ err });
         }
-
-        res.json({msg: 'Simulation removed'});
     });
+    res.json({ msg: 'Simulation removed' });
 });
 
 module.exports = simulationApp;
-- 
GitLab


From c755ce9a25fe7407f6b233c6d0b1c22d40ef0876 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 18 May 2023 11:52:17 -0300
Subject: [PATCH 023/123] [FIX] PUT Simulation

---
 src/libs/routes_v1/simulation.js | 46 +++++++++++++++++++-------------
 1 file changed, 27 insertions(+), 19 deletions(-)

diff --git a/src/libs/routes_v1/simulation.js b/src/libs/routes_v1/simulation.js
index ea9921d3..11ff69d3 100644
--- a/src/libs/routes_v1/simulation.js
+++ b/src/libs/routes_v1/simulation.js
@@ -38,37 +38,45 @@ simulationApp.get('/pqr', async (req, res) => {
 });
 
 simulationApp.put('/pqr', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-    let user = req.user.toObject();
-    let pqr = await PQR.findOne({ attributes: ['content'] }).catch(function (err) {
+    let user = req.user;
+    let pqr = await PQR.findOne().catch(function (err) {
         if (err) {
             log.error(err);
             return next({ err });
         }
     });
+    res.json(pqr);
     if (!user.admin) {
         log.info(`Usuário ${user.email} tentou alterar o PQR, mas não tem privilégio`);
         res.statusCode = 401;
         return next({ err: { msg: 'Unauthorized' } });
     }
-    pqr.content = req.body.content || pqr.content;
-    pqr.save().catch(function (err) {
-        if (err) {
-            log.error(err);
-            return next({ err });
-        }
-    });
+
+    if(pqr){
+        let _content = req.body.content || pqr.content;
+        pqr.content = _content;
+        await pqr.save({fields:['content']}).catch(function (err) {
+            if (err) {
+                log.error(err);
+                return next({ err });
+            }
+        });}
+    
     res.json({ msg: 'PQR updated' })
 
 });
 
 simulationApp.get('/', passport.authenticate('bearer', { session: false }), async (req, res) => {
-    let user = req.user.toObject();
+    let user = req.user;
     let simulations = await Simulation.findAll({ where: { user_id: user.id } }, { attributes: ['user_id', 'name', 'created_at', 'updated_at'] }).catch(function (err) {
         if (err) {
             log.error(err);
             return next({ err });
         }
     })
+    if (!simulations){
+        res.json("Simulations not found");
+    }
     res.json(simulations);
 
     // Simulation.find({userId: user._id}, (err, simulations) => {
@@ -82,7 +90,7 @@ simulationApp.get('/', passport.authenticate('bearer', { session: false }), asyn
 });
 
 simulationApp.post('/', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-    let user = req.user.toObject();
+    let user = req.user;
 
     let simulation = await Simulation.create({
         user_id: user.id,
@@ -101,9 +109,9 @@ simulationApp.post('/', passport.authenticate('bearer', { session: false }), asy
 })
 
 simulationApp.get('/:id', passport.authenticate('bearer', { session: false }), async (req, res) => {
-    let user = req.user.toObject();
+    let user = req.user;
 
-    let simulation = await Simulation.findOne({where:{id: req.params.id, userId: user._id }}).catch(function (err) {
+    let simulation = await Simulation.findOne({where:{id: req.params.id, user_id: user.id }}).catch(function (err) {
         if (err) {
             log.error(err);
             return next({ err });
@@ -114,7 +122,7 @@ simulationApp.get('/:id', passport.authenticate('bearer', { session: false }), a
 });
 
 simulationApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-    let user = req.user.toObject();
+    let user = req.user;
 
     let simulation = await Simulation.findOne({ where: { id: req.params.id, user_id: user.id } }).catch(function (err) {
         if (err) {
@@ -130,19 +138,19 @@ simulationApp.put('/:id', passport.authenticate('bearer', { session: false }), a
 
     simulation.content = req.body.content || simulation.content;
     simulation.name = req.body.name || simulation.name;
-    simulation.updated_at = Date.now();
-    simulation.save().catch(function (err) {
+    simulation.updated_at = new Date();
+    await simulation.save({fields:['content','name','updated_at']}).catch(function (err) {
         if (err) {
             log.error(err);
             return next({ err });
         }
     });
-    res.json(simulation);
+    res.json({msg: 'Simulation updated'});
 });
 
 simulationApp.delete('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-    let user = req.user.toObject();
-    await Simulation.destroy({ where: { "id": req.params.id, "user_id": user._id } }).catch(function (err) {
+    let user = req.user;
+    await Simulation.destroy({where: { "id": req.params.id, "user_id": user.id } }).catch(function (err) {
         if (err) {
             log.error(err);
             return next({ err });
-- 
GitLab


From 1e98205f1788c966b3a8908e6a5e596d72dde768 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Fri, 19 May 2023 11:00:16 -0300
Subject: [PATCH 024/123] [ADD] Downloads

---
 src/libs/middlewares/downloadDatabase.js | 78 +++++++++++-----------
 src/libs/models/download.js              | 83 +++++++++++++-----------
 src/libs/models/resetToken.js            |  3 +
 src/libs/routes_v1/downloads.js          | 53 +++++++--------
 src/libs/routes_v1/simulation.js         |  3 +-
 5 files changed, 112 insertions(+), 108 deletions(-)

diff --git a/src/libs/middlewares/downloadDatabase.js b/src/libs/middlewares/downloadDatabase.js
index c4672ff0..6f2dec6f 100644
--- a/src/libs/middlewares/downloadDatabase.js
+++ b/src/libs/middlewares/downloadDatabase.js
@@ -10,11 +10,11 @@ const config = require(`${libs}/config`);
 
 const Download = require(`${libs}/models/download`);
 
-module.exports = function download(table, mappingTable) {
+module.exports = function download(table, mapping_table) {
     return (req, res, next) => {
         // First, query the mapping
-        execute(`SELECT target_name, name FROM ${mappingTable}`, undefined, (err, result) => {
-            if(err) {
+        execute(`SELECT target_name, name FROM ${mapping_table}`, undefined, (err, result) => {
+            if (err) {
                 log.error(err.stack);
                 next(new Error('Request could not be satisfied due to a database error.'));
             } else {
@@ -23,7 +23,7 @@ module.exports = function download(table, mappingTable) {
                 result.forEach((field) => {
                     req.sql.field(`CASE ${table}.${field.name} WHEN true THEN 1 WHEN false THEN 0 ELSE ${table}.${field.name} END AS ${field.target_name}`);
                     // req.sql.field(table + '.' + field.name, field.target_name);
-                    if(header === '') header += field.target_name;
+                    if (header === '') header += field.target_name;
                     else header = header + ';' + field.target_name;
                 });
 
@@ -36,53 +36,51 @@ module.exports = function download(table, mappingTable) {
                     header
                 };
 
-                request.post(config.cdn.url + '/api/v1/file', {form}, (err, response, body) => {
-                    if(err) {
+                request.post(config.cdn.url + '/api/v1/file', { form }, async (err, response, body) => {
+                    if (err) {
                         log.error(err);
-                        return res.json({error: err});
+                        return res.json({ error: err });
                     }
 
-                    Download.findOne({query: req.sql.toString()}, (err, download) => {
-                        if(download) {
-                            download.updatedAt = Date.now();
-                            if(download.userId != req.user._id) {
-                                let dl = new Download({
-                                    userId: req.user._id,
-                                    table,
-                                    name: req.query.name,
-                                    mappingTable,
-                                    query: req.sql.toString(),
-                                    status: 'Enviando',
-                                    expired: false
-                                });
-                                console.log(dl);
-                                dl.save((err) => {
-                                    if(err) log.error(err);
-                                });
-                            }
-                        } else {
-                            download = new Download({
-                                userId: req.user._id,
+                    let download = await Download.findOne({ where: { query: req.sql.toString() } }).catch(function (err) {
+                        if (err) {
+                            log.error(err);
+                        }
+                    })
+                    if (download) {
+                        download.updated_at = new Date();
+                        if (download.user_id != req.user.id) {
+                            let dl = await Download.create({
+                                user_id: req.user.id,
                                 table,
                                 name: req.query.name,
-                                mappingTable,
-                                query: req.sql.toString(),
+                                mapping_table,
                                 query: req.sql.toString(),
                                 status: 'Enviando',
                                 expired: false
                             });
-                            console.log(download);
+                            console.log(dl);
+                            dl.save().catch(function (err) {
+                                if (err) log.error(err);
+                            });
                         }
-
-                        download.save((err) => {
-                            if(err) {
-                                log.error(err);
-                            }
-                            res.json({msg: 'Wait for download email', waitForIt: true});
+                    } else {
+                        download = await Download.create({
+                            user_id: req.user.id,
+                            table,
+                            name: req.query.name,
+                            mapping_table,
+                            query: req.sql.toString(),
+                            status: 'Enviando',
+                            expired: false
                         });
-                    });
+                        console.log(download);
+                    }
+
+                    await download.save()
+                    res.json({ msg: 'Wait for download email', waitForIt: true });
                 });
-            }
+            };
         });
     }
-};
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/src/libs/models/download.js b/src/libs/models/download.js
index 989896d9..3d59249a 100644
--- a/src/libs/models/download.js
+++ b/src/libs/models/download.js
@@ -1,53 +1,60 @@
-const mongoose = require('mongoose');
-const Schema = mongoose.Schema;
+const Sequelize = require("sequelize");
+const crypto = require('crypto');
+const db = require('../db/postgres.js');
 const libs = `${process.cwd()}/libs`;
-const log = require(`${libs}/log`)(module);
 const User = require(`${libs}/models/user`);
 
-let Download = new Schema({
-    userId: {
-        type: Schema.Types.ObjectId,
-        required: true,
-        ref: 'User'
-    },
-    table: {
-        type: String,
-        required: true
-    },
-    name: {
-        type: String,
-        required: true
-    },
-    mappingTable: {
-        type: String,
-        required: true
+var Download = db.define("Download",{
+    id:{
+        type: Sequelize.INTEGER, 
+        allowNull: false,
+        autoIncrement: true,
+        unique: true,
+        primaryKey: true
+    },
+    user_id: {
+        type: Sequelize.STRING,
+        allowNull: false
+    },
+    table:{
+        type: Sequelize.STRING, 
+        allowNull: false,
+    },
+    name:{
+        type: Sequelize.STRING,
+        allowNull: false,
+    },
+    mapping_table:{
+        type: Sequelize.STRING,
+        allowNull:false
     },
     query: {
-        type: String,
-        required: true
+        type: Sequelize.STRING,
+        allowNull:false
     },
-    createdAt: {
-        type: Date,
-        required: true,
-        default: Date.now
-    },
-    updatedAt: {
-        type: Date,
-        required: true,
-        default: Date.now
+    created_at:{
+        type: Sequelize.DATE,
+        defaultValue: Date.now
+    }, 
+    updated_at: {
+        type: Sequelize.DATE,
+        defaultValue: Date.now
     },
     status: {
-        type: String
+        type: Sequelize.STRING
     },
     size: {
-        type: Number
+        type: Sequelize.NUMBER
     },
     expired: {
-        type: Boolean
+        type: Sequelize.BOOLEAN
     },
     link: {
-        type: String
-    }
-});
+        type: Sequelize.STRING,
+    }},
+    {timestamps: false}
+);
+
+Download.hasOne(User,{ foreignKey: 'id' });
 
-module.exports = mongoose.model('Download', Download);
+module.exports = Download
diff --git a/src/libs/models/resetToken.js b/src/libs/models/resetToken.js
index 21e5915e..dca5488a 100644
--- a/src/libs/models/resetToken.js
+++ b/src/libs/models/resetToken.js
@@ -32,4 +32,7 @@ ResetToken.prototype.hasExpired = function () {
     return (now - this.createdAt) > 86400; //Expire if token is 1 day old
 };
 
+ResetToken.hasOne(User,{ foreignKey: 'id' });
+
+
 module.exports = ResetToken
diff --git a/src/libs/routes_v1/downloads.js b/src/libs/routes_v1/downloads.js
index 2ec83b9e..cd6cad0b 100644
--- a/src/libs/routes_v1/downloads.js
+++ b/src/libs/routes_v1/downloads.js
@@ -8,46 +8,43 @@ const log = require(`${libs}/log`)(module);
 
 const Download = require(`${libs}/models/download`);
 
-const User = require(`${libs}/models/user`);
-
 const passport = require('passport');
 
 const request = require(`request`);
 
 const config = require(`${libs}/config`);
 
-downloadApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, next) => {
-    request.get(config.cdn.url + '/api/v1/file', (err, response, body) => {
+downloadApp.get('/', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+    request.get(config.cdn.url + '/api/v1/file', async (err, response, body) => {
         let cdn = JSON.parse(body);
-        Download.find({userId: req.user._id}, (err, downloads) => {
+        const downloads = await Download.findAll({ where: { user_id: req.user.id } }).catch(function (err) {
             if (err) {
                 log.error(err);
                 return next(err);
             }
-            
-            if(!downloads) {
-                res.statusCode = 404;
-                return res.json({msg: 'Nenhum download encontrado'});
-            } else {
-                downloads.forEach((dl) => {
-                    for(let i = 0; i < cdn.length; ++i) {
-                        if(cdn[i].query == dl.query) {
-                            dl.status = cdn[i].expired ? 'Expirado' : 'Enviado';
-                            dl.size = cdn[i].size;
-                            dl.expired = cdn[i].expired;
-                            dl.updatedAt = cdn[i].lastAccess;
-                            dl.link = config.cdn.download + '/' + cdn[i]._id;
-
-                            dl.save((err) => {
-                                if(err) log.error(err);
-                            });
-                            return;
-                        }
-                    }
-                });
-            }
-            res.json(downloads);
         });
+        if (!downloads) {
+            res.statusCode = 404;
+            return res.json({ msg: 'Nenhum download encontrado' });
+        } else {
+            downloads.forEach((dl) => {
+                for (let i = 0; i < cdn.length; ++i) {
+                    if (cdn[i].query == dl.query) {
+                        dl.status = cdn[i].expired ? 'Expirado' : 'Enviado';
+                        dl.size = cdn[i].size;
+                        dl.expired = cdn[i].expired;
+                        dl.updatedAt = cdn[i].lastAccess;
+                        dl.link = config.cdn.download + '/' + cdn[i]._id;
+
+                        dl.save((err) => {
+                            if (err) log.error(err);
+                        });
+                        return;
+                    }
+                }
+            });
+        }
+        res.json(downloads);
     });
 });
 
diff --git a/src/libs/routes_v1/simulation.js b/src/libs/routes_v1/simulation.js
index 11ff69d3..cc0ab380 100644
--- a/src/libs/routes_v1/simulation.js
+++ b/src/libs/routes_v1/simulation.js
@@ -45,7 +45,6 @@ simulationApp.put('/pqr', passport.authenticate('bearer', { session: false }), a
             return next({ err });
         }
     });
-    res.json(pqr);
     if (!user.admin) {
         log.info(`Usuário ${user.email} tentou alterar o PQR, mas não tem privilégio`);
         res.statusCode = 401;
@@ -145,7 +144,7 @@ simulationApp.put('/:id', passport.authenticate('bearer', { session: false }), a
             return next({ err });
         }
     });
-    res.json({msg: 'Simulation updated'});
+    res.json({ msg: 'Simulation created', simulation });
 });
 
 simulationApp.delete('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-- 
GitLab


From 173e946cad4e61764504c77ec502801cf37222c1 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Tue, 23 May 2023 11:29:55 -0300
Subject: [PATCH 025/123] [ADD] New Tables and Relations in Postgres

---
 src/libs/models/Permission.js     | 0
 src/libs/models/PermissionRole.js | 0
 src/libs/models/Role.js           | 0
 src/libs/models/user.js           | 5 +++++
 4 files changed, 5 insertions(+)
 create mode 100644 src/libs/models/Permission.js
 create mode 100644 src/libs/models/PermissionRole.js
 create mode 100644 src/libs/models/Role.js

diff --git a/src/libs/models/Permission.js b/src/libs/models/Permission.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/libs/models/PermissionRole.js b/src/libs/models/PermissionRole.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/libs/models/Role.js b/src/libs/models/Role.js
new file mode 100644
index 00000000..e69de29b
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index a1ab8e27..f4a9d626 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -148,6 +148,11 @@ var User = db.define("User",{
         type: Sequelize.BOOLEAN,
         defaultValue:false
     },
+    role_id:{
+        type: Sequelize.NUMBER,
+        allowNull:false,
+        defaultValue: 0
+    }
 },
     {timestamps: false}
 );
-- 
GitLab


From 22047d80f4c5054d65229e4d659f9916aceedd28 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Tue, 23 May 2023 11:50:45 -0300
Subject: [PATCH 026/123] [ADD] Models

---
 src/libs/models/Permission.js     |  0
 src/libs/models/PermissionRole.js |  0
 src/libs/models/Role.js           |  0
 src/libs/models/permission.js     | 25 +++++++++++++++++++++++++
 src/libs/models/permissionRole.js | 29 +++++++++++++++++++++++++++++
 src/libs/models/role.js           | 19 +++++++++++++++++++
 src/libs/models/user.js           |  3 +++
 7 files changed, 76 insertions(+)
 delete mode 100644 src/libs/models/Permission.js
 delete mode 100644 src/libs/models/PermissionRole.js
 delete mode 100644 src/libs/models/Role.js
 create mode 100644 src/libs/models/permission.js
 create mode 100644 src/libs/models/permissionRole.js
 create mode 100644 src/libs/models/role.js

diff --git a/src/libs/models/Permission.js b/src/libs/models/Permission.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/libs/models/PermissionRole.js b/src/libs/models/PermissionRole.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/libs/models/Role.js b/src/libs/models/Role.js
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/libs/models/permission.js b/src/libs/models/permission.js
new file mode 100644
index 00000000..917b5197
--- /dev/null
+++ b/src/libs/models/permission.js
@@ -0,0 +1,25 @@
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
+const libs = `${process.cwd()}/libs`;
+const Role = require(`${libs}/models/role`);
+
+var Permission = db.define("PermissionRole",{
+    id:{
+        type: Sequelize.INTEGER,
+        allowNull:false,
+        unique: true,
+        primaryKey: true
+    },
+    permission_name:{
+        type: Sequelize.STRING,
+        allowNull:false,
+    },
+    permission_description:{
+        type: Sequelize.STRING,
+        allowNull:false
+    }
+},
+{timestamps: false});
+
+module.exports = PermissionRole;
+
diff --git a/src/libs/models/permissionRole.js b/src/libs/models/permissionRole.js
new file mode 100644
index 00000000..441b4345
--- /dev/null
+++ b/src/libs/models/permissionRole.js
@@ -0,0 +1,29 @@
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
+const libs = `${process.cwd()}/libs`;
+const Role = require(`${libs}/models/role`);
+const Permission = require(`${libs}/models/permission`);
+
+var PermissionRole = db.define("PermissionRole",{
+    id:{
+        type: Sequelize.INTEGER,
+        allowNull:false,
+        unique: true,
+        primaryKey: true
+    },
+    role_id:{
+        type: Sequelize.INTEGER,
+        allowNull:false,
+    },
+    permission_id:{
+        type: Sequelize.INTEGER,
+        allowNull:false
+    }
+},
+{timestamps: false});
+
+PermissionRole.hasOne(Role, {foreignKey: 'id'});
+PermissionRole.hasOne(Permission, {foreignKey: 'id'});
+
+module.exports = PermissionRole;
+
diff --git a/src/libs/models/role.js b/src/libs/models/role.js
new file mode 100644
index 00000000..dc30e09e
--- /dev/null
+++ b/src/libs/models/role.js
@@ -0,0 +1,19 @@
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
+
+var Role = db.define("Role",{
+    id:{
+        type: Sequelize.INTEGER,
+        allowNull:false,
+        unique: true,
+        primaryKey: true
+    },
+    role_name:{
+        type: Sequelize.STRING,
+        allowNull:false,
+    }
+},
+{timestamps: false});
+
+module.exports = Role;
+
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index f4a9d626..ce65500a 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -2,6 +2,7 @@ const Sequelize = require("sequelize");
 const crypto = require('crypto');
 const db = require('../db/postgres.js');
 const libs = `${process.cwd()}/libs`;
+const Role = require(`${libs}/models/role`);
 
 // set up a sequelize model 
 var User = db.define("User",{
@@ -157,6 +158,8 @@ var User = db.define("User",{
     {timestamps: false}
 );
 
+User.hasOne(Role, {foreignKey: 'id'});
+
 User.generateSalt = function() {
     return crypto.randomBytes(128).toString('hex');
 }
-- 
GitLab


From ecec214d999b5d2ef3f15a6e41ee426fd72bbbc4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Wed, 24 May 2023 11:08:00 -0300
Subject: [PATCH 027/123] Initiate changes to aggregated classroom_count, not
 ready yet

---
 src/libs/middlewares/query.js        |   2 +
 src/libs/routes_v2/aux               |   1 +
 src/libs/routes_v2/classroomCount.js | 145 +++++++++++----------------
 3 files changed, 61 insertions(+), 87 deletions(-)
 create mode 100644 src/libs/routes_v2/aux

diff --git a/src/libs/middlewares/query.js b/src/libs/middlewares/query.js
index b724d93d..3b734957 100644
--- a/src/libs/middlewares/query.js
+++ b/src/libs/middlewares/query.js
@@ -10,6 +10,8 @@ function query(req, res, next) {
     execute(sql.text, sql.values, (err, result) => {
         if(err) {
             log.error(err.stack);
+            console.log(sql.text);
+            console.log(sql.values);
             next(new Error('Request could not be satisfied due to a database error.'));
         } else {
             req.result = result;
diff --git a/src/libs/routes_v2/aux b/src/libs/routes_v2/aux
new file mode 100644
index 00000000..c0b4cc5f
--- /dev/null
+++ b/src/libs/routes_v2/aux
@@ -0,0 +1 @@
+SELECT COUNT(*) AS "total", simcaq_matricula_agregada.ano_censo AS "year", estado.nome AS "state_name", estado.id AS "state_id", municipio.nome AS "city_name", municipio.id AS "city_id", simcaq_matricula_agregada.etapa_resumida AS "education_level_short_id", simcaq_matricula_agregada.tempo_integral AS "integral_time_id" FROM simcaq_matricula_agregada INNER JOIN estado ON (simcaq_matricula_agregada.estado_id=estado.id) INNER JOIN municipio ON (simcaq_matricula_agregada.municipio_id=municipio.id) WHERE (simcaq_matricula_agregada.ano_censo >= 2021 ) AND (simcaq_matricula_agregada.ano_censo <= 2021 ) GROUP BY simcaq_matricula_agregada.ano_censo, estado.nome, estado.id, municipio.nome, municipio.id, simcaq_matricula_agregada.etapa_resumida, simcaq_matricula_agregada.tempo_integral ORDER BY simcaq_matricula_agregada.ano_censo ASC, estado.nome ASC, estado.id ASC, municipio.nome ASC, municipio.id ASC, simcaq_matricula_agregada.etapa_resumida ASC, simcaq_matricula_agregada.tempo_integral ASC
diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 4f76bed3..34c8b33f 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -54,21 +54,20 @@ rqf.addField({
 }).addValueToField({
     name: 'city',
     table: 'municipio',
-    tableField: 'nome',
-    resultField: 'city_name',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
     where: {
         relation: '=',
         type: 'integer',
-        field: 'municipio_id',
-        table: '@'
+        field: 'id'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
         foreignTable: '@'
     }
-}, 'filter').addValueToField({
-    name: 'city',
+}, 'dims').addValueToField({
+    name: 'cityTeacher',
     table: 'municipio',
     tableField: ['nome', 'id'],
     resultField: ['city_name', 'city_id'],
@@ -79,26 +78,10 @@ rqf.addField({
     },
     join: {
         primary: 'id',
-        foreign: 'municipio_id',
+        foreign: 'escola_cidade_id',
         foreignTable: '@'
     }
 }, 'dims').addValueToField({
-    name: 'state',
-    table: 'estado',
-    tableField: 'nome',
-    resultField: 'state_name',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'estado_id',
-        table: '@'
-    },
-    join: {
-        primary: 'id',
-        foreign: 'estado_id',
-        foreignTable: '@'
-    }
-}, 'filter').addValueToField({
     name: 'state',
     table: 'estado',
     tableField: ['nome', 'id'],
@@ -115,7 +98,7 @@ rqf.addField({
     }
 }, 'dims').addValueToField({
     name: 'school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: ['nome_escola', 'id'],
     resultField: ['school_name', 'school_id'],
     where: {
@@ -163,39 +146,9 @@ rqf.addField({
         type: 'integer',
         field: 'ano_censo'
     }
-}).addValue({
-    name: 'school_year',
-    table: '@',
-    tableField: 'serie_ano_id',
-    resultField: 'school_year_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'serie_ano_id'
-    }
-}).addValue({
-    name: 'location',
-    table: '@',
-    tableField: 'localizacao_id',
-    resultField: 'location_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'localizacao_id'
-    }
-}).addValue({
-    name: 'period',
-    table: '@',
-    tableField: 'turma_turno_id',
-    resultField: 'period_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'turma_turno_id'
-    }
 }).addValue({
     name: 'school_building',
-    table: 'escola',
+    table: '@',
     tableField: 'local_func_predio_escolar',
     resultField: 'school_building',
     where: {
@@ -205,7 +158,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'night_time',
-    table: 'matricula',
+    table: '@',
     tableField: 'periodo_noturno',
     resultField: 'night_time',
     where: {
@@ -215,7 +168,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'formation_level',
-    table: 'docente_por_formacao',
+    table: '@',
     tableField: 'tipo_formacao',
     resultField: 'formation_level',
     where: {
@@ -235,7 +188,7 @@ rqf.addField({
     }
 }, 'filter') .addValue({
     name: 'integral_time',
-    table: 'matricula',
+    table: '@',
     tableField: 'tempo_integral',
     resultField: 'integral_time_id',
     where: {
@@ -245,7 +198,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'education_level_short',
-    table: 'matricula',
+    table: '@',
     tableField: 'etapa_resumida',
     resultField: 'education_level_short_id',
     where: {
@@ -253,6 +206,26 @@ rqf.addField({
         type: 'integer',
         field: 'etapa_resumida'
     }
+}).addValue({
+    name: 'location',
+    table: '@',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'school_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'school_year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
 });
 
 classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
@@ -276,11 +249,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.sql.field('sum(dia_total)', 'total_day')
     .field('sum(noite_total)', 'total_night')
     .field("'Brasil'", 'name')
-    .field('matricula_por_localizacao.ano_censo', 'year')
-    .from('matricula_por_localizacao')
-    .where('matricula_por_localizacao.serie_ano_id < 15')
-    .group('matricula_por_localizacao.ano_censo')
-    .order('matricula_por_localizacao.ano_censo')
+    .field('simcaq_matricula_por_localizacao.ano_censo', 'year')
+    .from('simcaq_matricula_por_localizacao')
+    .where('simcaq_matricula_por_localizacao.serie_ano_id < 15')
+    .group('simcaq_matricula_por_localizacao.ano_censo')
+    .order('simcaq_matricula_por_localizacao.ano_censo')
 
     next();
 }, rqf.build(), query, id2str.transform(), (req, res, next) => {
@@ -310,7 +283,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             };
         }
     }
-    
+
     delete req.dims;
     delete req.filter;
     req.resetSql();
@@ -322,15 +295,15 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.dims.location = true;
     req.dims.school_building = true;
 
-    req.sql.field('SUM(escola.qtde_salas_utilizadas_dentro)', 'total')
+    req.sql.field('SUM(escola_agregada.qtde_salas_utilizadas_dentro)', 'total')
     .field("'Brasil'", 'name')
-    .field('escola.ano_censo', 'year')
-    .from('escola')
-    .group('escola.ano_censo')
-    .order('escola.ano_censo')
-    .where('escola.situacao_de_funcionamento = 1')
-    .where('escola.dependencia_adm_id < 4') 
-    .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1');
+    .field('escola_agregada.ano_censo', 'year')
+    .from('escola_agregada')
+    .group('escola_agregada.ano_censo')
+    .order('escola_agregada.ano_censo')
+    .where('escola_agregada.situacao_funcionamento_pareada = 1')
+    .where('escola_agregada.dependencia_adm_id < 4') 
+    .where('escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1');
 
     next();
 }, rqf.build(), query, id2str.transform(), (req, res, next) =>  {
@@ -379,15 +352,14 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         req.teacherCalc = true;
     }
 
-    req.dims.state = true;
-    req.dims.city = true;
+    req.dims.cityTeacher = true;
     req.dims.formation_level = true;
-    req.sql.field('count(distinct docente_por_formacao.id_docente)', 'total')
+    req.sql.field('SUM(simcaq_docente_agregada.num_docentes)', 'total')
            .field("'Brasil'", 'name')
-           .field('docente_por_formacao.ano_censo', 'year')
-           .from('docente_por_formacao')
-           .group('docente_por_formacao.ano_censo')
-           .order('docente_por_formacao.ano_censo');
+           .field('simcaq_docente_agregada.ano_censo', 'year')
+           .from('simcaq_docente_agregada')
+           .group('simcaq_docente_agregada.ano_censo')
+           .order('simcaq_docente_agregada.ano_censo');
 
     next();
 }, rqf.build(), query, id2str.transform(), (req, res, next) =>  {
@@ -401,13 +373,13 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.dims.state = true;
     req.dims.city = true;
     req.dims.education_level_short = true;
-    req.dims.integral_time = true;
+    //req.dims.integral_time = true;
     req.sql.field('COUNT(*)', 'total')
-           .field('matricula.ano_censo', 'year')
-           .from('matricula')
-           .group('matricula.ano_censo')
-           .order('matricula.ano_censo')
-           .where('((matricula.tipo<=3 OR matricula.tipo IS NULL) AND (matricula.tipo_atendimento_turma IS NULL OR matricula.tipo_atendimento_turma <= 2) AND matricula.turma_turno_id <> 99)');
+           .field('simcaq_matricula_agregada.ano_censo', 'year')
+           .from('simcaq_matricula_agregada')
+           .group('simcaq_matricula_agregada.ano_censo')
+           .order('simcaq_matricula_agregada.ano_censo');
+           //.where('((simcaq_matricula_agregada.tipo<=3 OR simcaq_matricula_agregada.tipo IS NULL) AND (simcaq_matricula_agregada.tipo_atendimento_turma IS NULL OR simcaq_matricula_agregada.tipo_atendimento_turma <= 2) AND simcaq_matricula_agregada.turma_turno_id <> 99)');
     next();
 }, rqf.build() ,query, id2str.transform(), (req, res, next) => {
     // constrói objeto de tempo integral e calcula diagnósticos
@@ -562,7 +534,6 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                     ti++;
                 
                 if (ti === req.teacher.length) {
-                    console.log(classroom[id_attribute], "not found")
                     while (classroom[id_attribute] === enrollments[j][id_attribute])
                         enrollments.splice(j, 1)
                     ti = old_ti; 
-- 
GitLab


From 04449c13aa6ea709ef3d5133c93b6e8028a8eee5 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 25 May 2023 11:56:36 -0300
Subject: [PATCH 028/123] [ADD] Models Publication, userPublication

---
 src/libs/models/permissionRole.js  |   4 +-
 src/libs/models/publication.js     |  57 +++++++
 src/libs/models/userPublication.js |  29 ++++
 src/libs/routes_v1/publication.js  | 266 +++++++++++++++++++++++++++++
 src/libs/routes_v1/user.js         |   3 +-
 5 files changed, 356 insertions(+), 3 deletions(-)
 create mode 100644 src/libs/models/publication.js
 create mode 100644 src/libs/models/userPublication.js
 create mode 100644 src/libs/routes_v1/publication.js

diff --git a/src/libs/models/permissionRole.js b/src/libs/models/permissionRole.js
index 441b4345..e415acc6 100644
--- a/src/libs/models/permissionRole.js
+++ b/src/libs/models/permissionRole.js
@@ -22,8 +22,8 @@ var PermissionRole = db.define("PermissionRole",{
 },
 {timestamps: false});
 
-PermissionRole.hasOne(Role, {foreignKey: 'id'});
-PermissionRole.hasOne(Permission, {foreignKey: 'id'});
+PermissionRole.hasMany(Role, {foreignKey: 'id'});
+PermissionRole.hasMany(Permission, {foreignKey: 'id'});
 
 module.exports = PermissionRole;
 
diff --git a/src/libs/models/publication.js b/src/libs/models/publication.js
new file mode 100644
index 00000000..03b3f0f4
--- /dev/null
+++ b/src/libs/models/publication.js
@@ -0,0 +1,57 @@
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
+const libs = `${process.cwd()}/libs`;
+
+var Publication = db.define("Publication",{
+    id:{
+        type: Sequelize.INTEGER,
+        allowNull:false,
+        unique: true,
+        primaryKey: true
+    },
+    filter:{
+        type: Sequelize.ENUM("Artigo", "Tese", "Dissertação", "Relatório", "Periódico"),
+        allowNull:false,
+        validate: {
+            notNull: { msg: "O campo categoria é obrigatória e aceita apenas os valores 'Artigo', 'Tese', 'Dissertação', 'Relatório', 'Periódico."},
+        }
+    },
+    title:{
+        type: Sequelize.STRING,
+        allowNull:false,
+    },
+    authors:{
+        type: Sequelize.STRING,
+        allowNull:false
+    },
+    organization:{
+        type: Sequelize.STRING,
+        allowNull:false
+    },
+    year:{
+        type:Sequelize.STRING,
+        allowNull:false
+    },
+    text:{ 
+        type: Sequelize.ENUM("Baixar","Acessar"),
+        allowNull:false,
+        validate: {
+            notNull: { msg: "O campo origem é obrigatória e aceita apenas os valores 'Baixar', 'Acessar'."},
+        }
+    },
+    link:{
+        type: Sequelize.STRING
+    },
+    upload:{
+        type: Sequelize.STRING
+    },
+    is_draft:{
+        type:Sequelize.BOOLEAN,
+        allowNull:false,
+        defaultValue: true
+    }
+
+},
+{timestamps: false});
+
+module.exports = Publication;
\ No newline at end of file
diff --git a/src/libs/models/userPublication.js b/src/libs/models/userPublication.js
new file mode 100644
index 00000000..43417ffe
--- /dev/null
+++ b/src/libs/models/userPublication.js
@@ -0,0 +1,29 @@
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
+const libs = `${process.cwd()}/libs`;
+const User = require(`${libs}/models/user`);
+const Publication = require(`${libs}/models/publication`);
+
+var userPublication = db.define("userPublication",{
+    id:{
+        type: Sequelize.INTEGER,
+        allowNull:false,
+        unique: true,
+        primaryKey: true
+    },
+    user_id:{
+        type: Sequelize.STRING,
+        allowNull:false,
+    },
+    publication_id:{
+        type: Sequelize.INTEGER,
+        allowNull:false
+    }
+},
+{timestamps: false});
+
+userPublication.hasMany(User, {foreignKey: 'id'});
+userPublication.hasMany(Publication, {foreignKey: 'id'});
+
+module.exports = userPublication;
+
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
new file mode 100644
index 00000000..dc6d6471
--- /dev/null
+++ b/src/libs/routes_v1/publication.js
@@ -0,0 +1,266 @@
+const express = require('express');
+
+const pubApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const config = require(`${libs}/config`);
+
+const log = require(`${libs}/log`)(module);
+
+const VerificationToken = require(`${libs}/models/verificationToken`);
+
+const Publication = require(`${libs}/models/publication`);
+
+const UserPublication = require(`${libs}/models/userPublication`);
+
+const ResetToken = require(`${libs}/models/resetToken`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const email = require(`${libs}/middlewares/email`);
+
+const passport = require('passport');
+
+const uuid = require('node-uuid');
+
+function emailSyntax(email) {
+  const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
+  return regex.test(email);
+}
+
+pubApp.get('/', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  let up = await UserPublication.findAll({where:{user_id:req.user.id}}).catch(function(err){
+    if(err){
+        log.error(err);
+        return next(err);
+    }
+  });
+  console.log(up);
+  let publications = [];
+  for(let _id in up.publication_id){
+    let pb = await Publication.findByPk(_id).catch(function(err){
+        if(err){
+            log.error(err);
+            return next(err);
+        }
+      });
+    publications.push(pb);
+  }
+  next();
+}, response('user'));
+
+pubApp.get('/:id', (req, res, next) => {
+  UserPublication.findByPk(req.params.id).then((user) => {
+    if (!user) {
+      res.statusCode = 404;
+      res.json({ msg: "O usuário não está cadastrado" });
+    } else {
+      let u = user.toJSON();
+      delete u.hashed_password;
+      delete u.salt;
+      req.result = u;
+      next();
+    }
+  }).catch(function (err) {
+    log.error(err);
+    return next(err);
+  });
+}, response('publication'));
+
+pubApp.post('/', async (req, res, next) => {
+  if (typeof req.body.password === 'undefined' || !req.body.password) {
+    res.statusCode = 400;
+    return res.json({ errors: ["O campo senha é obrigatório"] });
+  } else {
+    let user = await User.create({
+      id: 0,
+      email: req.body.email,
+      password: req.body.password,
+      hashed_password: 0,
+      salt: 0,
+      name: req.body.name,
+      nickname: req.body.nickname,
+      cpf: req.body.cpf,
+      cep: req.body.cep,
+      complement: req.body.complement,
+      address: req.body.address,
+      phone: req.body.phone,
+      schooling: req.body.schooling,
+      course: req.body.course,
+      segment: req.body.segment,
+      role: req.body.role,
+      institution_name: req.body.institutionName,
+      state: req.body.state,
+      city: req.body.city,
+      receiveEmails: false || req.body.receiveEmails,
+      origin: req.body.origin,
+      citesegment: req.body.citesegment,
+      citerole: req.body.citerole,
+      admin: false,
+      role_id: 0
+    }).catch(function (err) {
+      log.error(err);
+      let errors = [];
+      for (let errName in err.errors) {
+        errors.push(err.errors[errName].message);
+      }
+      log.error(errors);
+      res.statusCode = 400;
+      return res.json({ err, errors });
+      // handle error;
+    });
+    let tokenValue = uuid.v4();
+    const verificationToken = VerificationToken.create({
+      user_id: user.id,
+      token: tokenValue,
+      verified: false
+    });
+    if (!verificationToken) {
+      res.statusCode = 404;
+      return res.json({ msg: "Couldn't create Verification Token" });
+    }
+    let url = config.default.lde.url + '/verify';
+    let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${tokenValue}`;
+    // Send confirmation email
+    let mailOptions = {
+      to: `"${user.name} <${user.email}>"`,
+      subject: "Confirme seu cadastro - Laboratório de Dados Educacionais",
+      text
+    }
+    email(mailOptions, (err, info) => {
+      if (err) {
+        log.error(err);
+        res.json({ msg: 'Message not delivered, user created but not confirmed' });
+      }
+      if (info) {
+        log.info(`Message ${info.messageId} sent: ${info.response}`);
+        log.info(`Usuário ${user.email} foi criado`);
+      }
+      res.json({ msg: 'User created' });
+    });
+  }
+});
+
+pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  let user = await User.findByPk(req.params.id).catch(function (err) {
+    if (err) {
+      log.error(err);
+      return next({ err });
+    }
+  })
+  if (!user) {
+    res.statusCode = 404;
+    return next({
+      err: {
+        message: 'Usuário não encontrado'
+      }
+    });
+  }
+
+  user.email = req.body.email || user.email;
+  user.name = req.body.name || user.name;
+  user.nickname = req.body.nickname || user.nickname || user.name;
+  user.cep = req.body.cep || user.cep;
+  user.complement = req.body.complement || user.complement;
+  user.address = req.body.address || user.address;
+  user.phone = req.body.phone || user.phone;
+  user.schooling = req.body.schooling || user.schooling;
+  user.course = req.body.course || user.course;
+  user.segment = req.body.segment || user.segment;
+  user.role = req.body.role || user.role;
+  user.institutionName = req.body.institutionName || user.institutionName;
+  user.state = req.body.state || user.state;
+  user.city = req.body.city || user.city;
+  user.receiveEmails = req.body.receiveEmails || user.receiveEmails;
+  user.citesegment = req.body.citesegment || user.citesegment;
+  user.citerole = req.body.citerole || user.citerole;
+
+
+
+  if ((req.body.password) && (req.body.newpassword)) {
+    if (req.body.password != req.body.newpassword) {
+      if (user.checkPassword(user, req.body.password)) {
+          await user.update({password:req.body.newpassword});
+        } 
+      else {
+        res.statusCode = 500;
+        return res.json({
+          error: {
+            message: 'A senha atual está incorreta'
+          }
+        });
+      }
+    } else {
+      res.statusCode = 500;
+      return res.json({
+        error: {
+          message: 'A nova senha é a mesma da senha atual'
+        }
+      });
+    }
+  }
+
+  user.save().catch(err => {
+    if (err) {
+      log.error(err);
+      return next({ message: 'Erro ao atualizar usuário' });
+    }})
+  let u = user.toJSON();
+  delete u.hashed_password;
+  delete u.salt;
+  delete u.password;
+  res.json({ user: u });
+
+});
+
+
+pubApp.get('/reset/password', async (req, res, next) => {
+  let emailAddress = req.query.email;
+  let user = await User.findOne({ where: { email: emailAddress } }).catch(function (err) {
+    log.error(err);
+    let errors = [];
+    for (let errName in err.errors) {
+      errors.push(err.errors[errName].message);
+    }
+    log.error(errors);
+    res.statusCode = 400;
+    return res.json({ err, errors });
+    // handle error;
+  });
+  if (!user) {
+    res.statusCode = 404;
+    res.json({ msg: "O usuário não está cadastrado" });
+  }
+  else {
+    let tokenValue = uuid.v4();
+    const rt = await ResetToken.create({
+      user_id: user.id,
+      token: tokenValue,
+      reset: false
+    });
+    if (!rt) {
+      res.statusCode = 404;
+      return res.json({ msg: "Couldn't create Reset Password Token" });
+    }
+    let url = config.default.lde.url + '/reset-password';
+    let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${tokenValue}`;
+    let mailOptions = {
+      to: `"${user.name} <${user.email}>"`,
+      subject: "Redefinição de Senha - Laboratório de Dados Educacionais",
+      text
+    }
+    console.log(mailOptions);
+    email(mailOptions, (err, info) => {
+      if (err) {
+        console.log(err);
+        log.error(err);
+        res.json({ msg: 'Undelivered Reset Password Mail' });
+      }
+      log.info(`Message ${info.messageId} sent: ${info.response}`);
+      res.json({ msg: 'Reset Password Mail Successfully Delivered' });
+    });
+  }
+});
+
+module.exports = pubApp;
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index d02edd84..eedf07b5 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -148,7 +148,8 @@ userApp.post('/', async (req, res, next) => {
       origin: req.body.origin,
       citesegment: req.body.citesegment,
       citerole: req.body.citerole,
-      admin: false
+      admin: false,
+      role_id: 0
     }).catch(function (err) {
       log.error(err);
       let errors = [];
-- 
GitLab


From e0ecd70789841eca4fbcfeeca6760561081af1fb Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Fri, 26 May 2023 11:48:36 -0300
Subject: [PATCH 029/123] [FIX] ID Type

---
 src/libs/models/permission.js      |  16 +++-
 src/libs/models/publication.js     |  15 ++-
 src/libs/models/userPublication.js |   2 +-
 src/libs/routes_v1/publication.js  | 144 ++++++++++++++---------------
 4 files changed, 98 insertions(+), 79 deletions(-)

diff --git a/src/libs/models/permission.js b/src/libs/models/permission.js
index 917b5197..9d827d30 100644
--- a/src/libs/models/permission.js
+++ b/src/libs/models/permission.js
@@ -5,7 +5,7 @@ const Role = require(`${libs}/models/role`);
 
 var Permission = db.define("PermissionRole",{
     id:{
-        type: Sequelize.INTEGER,
+        type: Sequelize.STRING,
         allowNull:false,
         unique: true,
         primaryKey: true
@@ -21,5 +21,19 @@ var Permission = db.define("PermissionRole",{
 },
 {timestamps: false});
 
+Permission.generateObjectId = function(){
+    var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
+    return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function() {
+        return (Math.random() * 16 | 0).toString(16);
+    }).toLowerCase();
+}
+
+const setObjectId = permission => {
+   permission.id = Permission.generateObjectId()
+};
+
+Permission.beforeCreate(setObjectId);
+
+
 module.exports = PermissionRole;
 
diff --git a/src/libs/models/publication.js b/src/libs/models/publication.js
index 03b3f0f4..1b19ceb7 100644
--- a/src/libs/models/publication.js
+++ b/src/libs/models/publication.js
@@ -4,7 +4,7 @@ const libs = `${process.cwd()}/libs`;
 
 var Publication = db.define("Publication",{
     id:{
-        type: Sequelize.INTEGER,
+        type: Sequelize.STRING,
         allowNull:false,
         unique: true,
         primaryKey: true
@@ -54,4 +54,17 @@ var Publication = db.define("Publication",{
 },
 {timestamps: false});
 
+Publication.generateObjectId = function(){
+    var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
+    return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function() {
+        return (Math.random() * 16 | 0).toString(16);
+    }).toLowerCase();
+}
+
+const setObjectId = pb => {
+   pb.id = Publication.generateObjectId()
+};
+
+Publication.beforeCreate(setObjectId);
+
 module.exports = Publication;
\ No newline at end of file
diff --git a/src/libs/models/userPublication.js b/src/libs/models/userPublication.js
index 43417ffe..654dd3c3 100644
--- a/src/libs/models/userPublication.js
+++ b/src/libs/models/userPublication.js
@@ -16,7 +16,7 @@ var userPublication = db.define("userPublication",{
         allowNull:false,
     },
     publication_id:{
-        type: Sequelize.INTEGER,
+        type: Sequelize.STRING,
         allowNull:false
     }
 },
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index dc6d6471..442af7ec 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -48,18 +48,15 @@ pubApp.get('/', passport.authenticate('bearer', { session: false }), async (req,
     publications.push(pb);
   }
   next();
-}, response('user'));
+}, response('publications'));
 
 pubApp.get('/:id', (req, res, next) => {
-  UserPublication.findByPk(req.params.id).then((user) => {
-    if (!user) {
+  Publication.findByPk(req.params.id).then((pb) => {
+    if (!pb) {
       res.statusCode = 404;
-      res.json({ msg: "O usuário não está cadastrado" });
+      res.json({ msg: "A publicação não está cadastrada" });
     } else {
-      let u = user.toJSON();
-      delete u.hashed_password;
-      delete u.salt;
-      req.result = u;
+      req.result = pb.toJSON();
       next();
     }
   }).catch(function (err) {
@@ -68,78 +65,73 @@ pubApp.get('/:id', (req, res, next) => {
   });
 }, response('publication'));
 
-pubApp.post('/', async (req, res, next) => {
-  if (typeof req.body.password === 'undefined' || !req.body.password) {
+pubApp.post('/', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  let pb = await Publication.create({
+    id: 0,
+    email: req.body.email,
+    password: req.body.password,
+    hashed_password: 0,
+    salt: 0,
+    name: req.body.name,
+    nickname: req.body.nickname,
+    cpf: req.body.cpf,
+    cep: req.body.cep,
+    complement: req.body.complement,
+    address: req.body.address,
+    phone: req.body.phone,
+    schooling: req.body.schooling,
+    course: req.body.course,
+    segment: req.body.segment,
+    role: req.body.role,
+    institution_name: req.body.institutionName,
+    state: req.body.state,
+    city: req.body.city,
+    receiveEmails: false || req.body.receiveEmails,
+    origin: req.body.origin,
+    citesegment: req.body.citesegment,
+    citerole: req.body.citerole,
+    admin: false,
+    role_id: 0
+  }).catch(function (err) {
+    log.error(err);
+    let errors = [];
+    for (let errName in err.errors) {
+      errors.push(err.errors[errName].message);
+    }
+    log.error(errors);
     res.statusCode = 400;
-    return res.json({ errors: ["O campo senha é obrigatório"] });
-  } else {
-    let user = await User.create({
-      id: 0,
-      email: req.body.email,
-      password: req.body.password,
-      hashed_password: 0,
-      salt: 0,
-      name: req.body.name,
-      nickname: req.body.nickname,
-      cpf: req.body.cpf,
-      cep: req.body.cep,
-      complement: req.body.complement,
-      address: req.body.address,
-      phone: req.body.phone,
-      schooling: req.body.schooling,
-      course: req.body.course,
-      segment: req.body.segment,
-      role: req.body.role,
-      institution_name: req.body.institutionName,
-      state: req.body.state,
-      city: req.body.city,
-      receiveEmails: false || req.body.receiveEmails,
-      origin: req.body.origin,
-      citesegment: req.body.citesegment,
-      citerole: req.body.citerole,
-      admin: false,
-      role_id: 0
-    }).catch(function (err) {
+    return res.json({ err, errors });
+    // handle error;
+  });
+  let tokenValue = uuid.v4();
+  const verificationToken = VerificationToken.create({
+    user_id: user.id,
+    token: tokenValue,
+    verified: false
+  });
+  if (!verificationToken) {
+    res.statusCode = 404;
+    return res.json({ msg: "Couldn't create Verification Token" });
+  }
+  let url = config.default.lde.url + '/verify';
+  let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${tokenValue}`;
+  // Send confirmation email
+  let mailOptions = {
+    to: `"${user.name} <${user.email}>"`,
+    subject: "Confirme seu cadastro - Laboratório de Dados Educacionais",
+    text
+  }
+  email(mailOptions, (err, info) => {
+    if (err) {
       log.error(err);
-      let errors = [];
-      for (let errName in err.errors) {
-        errors.push(err.errors[errName].message);
-      }
-      log.error(errors);
-      res.statusCode = 400;
-      return res.json({ err, errors });
-      // handle error;
-    });
-    let tokenValue = uuid.v4();
-    const verificationToken = VerificationToken.create({
-      user_id: user.id,
-      token: tokenValue,
-      verified: false
-    });
-    if (!verificationToken) {
-      res.statusCode = 404;
-      return res.json({ msg: "Couldn't create Verification Token" });
+      res.json({ msg: 'Message not delivered, user created but not confirmed' });
     }
-    let url = config.default.lde.url + '/verify';
-    let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${tokenValue}`;
-    // Send confirmation email
-    let mailOptions = {
-      to: `"${user.name} <${user.email}>"`,
-      subject: "Confirme seu cadastro - Laboratório de Dados Educacionais",
-      text
+    if (info) {
+      log.info(`Message ${info.messageId} sent: ${info.response}`);
+      log.info(`Usuário ${user.email} foi criado`);
     }
-    email(mailOptions, (err, info) => {
-      if (err) {
-        log.error(err);
-        res.json({ msg: 'Message not delivered, user created but not confirmed' });
-      }
-      if (info) {
-        log.info(`Message ${info.messageId} sent: ${info.response}`);
-        log.info(`Usuário ${user.email} foi criado`);
-      }
-      res.json({ msg: 'User created' });
-    });
-  }
+    res.json({ msg: 'User created' });
+  });
 });
 
 pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-- 
GitLab


From 63ee99422fcf801a928913e2fb00f772728fe887 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Wed, 31 May 2023 09:43:09 -0300
Subject: [PATCH 030/123] Add changes to classroom count v2

---
 src/libs/routes_v2/aux               | 1 -
 src/libs/routes_v2/classroomCount.js | 9 +++++----
 2 files changed, 5 insertions(+), 5 deletions(-)
 delete mode 100644 src/libs/routes_v2/aux

diff --git a/src/libs/routes_v2/aux b/src/libs/routes_v2/aux
deleted file mode 100644
index c0b4cc5f..00000000
--- a/src/libs/routes_v2/aux
+++ /dev/null
@@ -1 +0,0 @@
-SELECT COUNT(*) AS "total", simcaq_matricula_agregada.ano_censo AS "year", estado.nome AS "state_name", estado.id AS "state_id", municipio.nome AS "city_name", municipio.id AS "city_id", simcaq_matricula_agregada.etapa_resumida AS "education_level_short_id", simcaq_matricula_agregada.tempo_integral AS "integral_time_id" FROM simcaq_matricula_agregada INNER JOIN estado ON (simcaq_matricula_agregada.estado_id=estado.id) INNER JOIN municipio ON (simcaq_matricula_agregada.municipio_id=municipio.id) WHERE (simcaq_matricula_agregada.ano_censo >= 2021 ) AND (simcaq_matricula_agregada.ano_censo <= 2021 ) GROUP BY simcaq_matricula_agregada.ano_censo, estado.nome, estado.id, municipio.nome, municipio.id, simcaq_matricula_agregada.etapa_resumida, simcaq_matricula_agregada.tempo_integral ORDER BY simcaq_matricula_agregada.ano_censo ASC, estado.nome ASC, estado.id ASC, municipio.nome ASC, municipio.id ASC, simcaq_matricula_agregada.etapa_resumida ASC, simcaq_matricula_agregada.tempo_integral ASC
diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 34c8b33f..fbac836a 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -250,9 +250,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     .field('sum(noite_total)', 'total_night')
     .field("'Brasil'", 'name')
     .field('simcaq_matricula_por_localizacao.ano_censo', 'year')
+    .field('simcaq_matricula_por_localizacao.serie_ano_id', 'school_year_id')
     .from('simcaq_matricula_por_localizacao')
     .where('simcaq_matricula_por_localizacao.serie_ano_id < 15')
     .group('simcaq_matricula_por_localizacao.ano_censo')
+    .group('simcaq_matricula_por_localizacao.serie_ano_id')
     .order('simcaq_matricula_por_localizacao.ano_censo')
 
     next();
@@ -387,7 +389,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.integral_time = {}
     for (let i = 0; i < integral_time_result.length; ++i){
         // Se cidade não foi criada, cria
-        let integral_time = integral_time_result[i];
+        let integral_time = integral_time_result[i]
         let code = '' + integral_time.year + integral_time.city_id
         if (req.dims.school) code = code + integral_time.school_id
 
@@ -587,13 +589,12 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 let enrollmentEducationLevel = req.educationSchoolYear[enrollment.school_year_id];
                 // Se não há um número de alunos por turna para a etapa de ensino, ignoramos a entrada
 
-                if(enrollmentEducationLevel.numberStudentClass == null) continue;
+                if(typeof enrollmentEducationLevel.numberStudentClass == 'undefined' || enrollmentEducationLevel.numberStudentClass == null) continue;
                 
                 // Adiciona nível de educação para município/escola
                 let educationLevel = null;
                 if(!educationLevelSet.has(enrollmentEducationLevel.id)) { // cria e insere ordenadamente novo education level
                     educationLevelSet.add(enrollmentEducationLevel.id);
-                    
                     let itHash = '' + enrollment.year + enrollment.city_id
                     if (req.dims.school) itHash += enrollment.school_id
     
@@ -660,7 +661,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 let currentSchoolYear = null;
                 if(enrollmentEducationLevel.id == 1){
                     let schoolYearHash = '' + enrollment.year + enrollment.city_id + enrollment.location_id + enrollment.school_year_id;
-                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.shcool_id
+                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.school_id
 
                     if(schoolYearSet.has(schoolYearHash)) { // Busca a série escolar
                         let k = 0;
-- 
GitLab


From 0e82c90bd8ebbafc9052aaa8390d617d33ad32d9 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 31 May 2023 10:23:06 -0300
Subject: [PATCH 031/123] [ADD] Publication POST

---
 src/libs/models/publication.js    |  5 ++
 src/libs/routes_v1/api.js         |  5 ++
 src/libs/routes_v1/publication.js | 93 +++++++++----------------------
 3 files changed, 36 insertions(+), 67 deletions(-)

diff --git a/src/libs/models/publication.js b/src/libs/models/publication.js
index 1b19ceb7..ec3ed5c0 100644
--- a/src/libs/models/publication.js
+++ b/src/libs/models/publication.js
@@ -49,6 +49,11 @@ var Publication = db.define("Publication",{
         type:Sequelize.BOOLEAN,
         allowNull:false,
         defaultValue: true
+    },
+    is_headline:{
+        type:Sequelize.BOOLEAN,
+        allowNull: false,
+        defaultValue: true
     }
 
 },
diff --git a/src/libs/routes_v1/api.js b/src/libs/routes_v1/api.js
index 41fe392c..50941a94 100644
--- a/src/libs/routes_v1/api.js
+++ b/src/libs/routes_v1/api.js
@@ -137,6 +137,8 @@ const message = require(`${libs}/routes_v1/message`);
 
 const courseStudents = require(`${libs}/routes_v1/courseStudents`);
 
+const publication = require(`${libs}/routes_v1/publication`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v1 is running' });
 });
@@ -197,4 +199,7 @@ api.use('/universityLocalOffer', universityLocalOffer);
 api.use('/message', message);
 api.use('/course_students', courseStudents);
 
+//Publication 
+api.use('/publication', publication);
+
 module.exports = api;
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 442af7ec..dcb02ce5 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -30,21 +30,21 @@ function emailSyntax(email) {
 }
 
 pubApp.get('/', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-  let up = await UserPublication.findAll({where:{user_id:req.user.id}}).catch(function(err){
-    if(err){
-        log.error(err);
-        return next(err);
+  let up = await UserPublication.findAll({ where: { user_id: req.user.id } }).catch(function (err) {
+    if (err) {
+      log.error(err);
+      return next(err);
     }
   });
   console.log(up);
   let publications = [];
-  for(let _id in up.publication_id){
-    let pb = await Publication.findByPk(_id).catch(function(err){
-        if(err){
-            log.error(err);
-            return next(err);
-        }
-      });
+  for (let _id in up.publication_id) {
+    let pb = await Publication.findByPk(_id).catch(function (err) {
+      if (err) {
+        log.error(err);
+        return next(err);
+      }
+    });
     publications.push(pb);
   }
   next();
@@ -68,30 +68,15 @@ pubApp.get('/:id', (req, res, next) => {
 pubApp.post('/', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
   let pb = await Publication.create({
     id: 0,
-    email: req.body.email,
-    password: req.body.password,
-    hashed_password: 0,
-    salt: 0,
-    name: req.body.name,
-    nickname: req.body.nickname,
-    cpf: req.body.cpf,
-    cep: req.body.cep,
-    complement: req.body.complement,
-    address: req.body.address,
-    phone: req.body.phone,
-    schooling: req.body.schooling,
-    course: req.body.course,
-    segment: req.body.segment,
-    role: req.body.role,
-    institution_name: req.body.institutionName,
-    state: req.body.state,
-    city: req.body.city,
-    receiveEmails: false || req.body.receiveEmails,
-    origin: req.body.origin,
-    citesegment: req.body.citesegment,
-    citerole: req.body.citerole,
-    admin: false,
-    role_id: 0
+    filter: req.body.categoria,
+    title: req.body.title,
+    authors: req.body.autores,
+    organization: req.body.organizacao,
+    year: req.body.ano,
+    text: req.body.texto,
+    link: req.body.link,
+    upload: req.body.upload,
+    is_homepage: req.body.homepage
   }).catch(function (err) {
     log.error(err);
     let errors = [];
@@ -103,35 +88,8 @@ pubApp.post('/', passport.authenticate('bearer', { session: false }), async (req
     return res.json({ err, errors });
     // handle error;
   });
-  let tokenValue = uuid.v4();
-  const verificationToken = VerificationToken.create({
-    user_id: user.id,
-    token: tokenValue,
-    verified: false
-  });
-  if (!verificationToken) {
-    res.statusCode = 404;
-    return res.json({ msg: "Couldn't create Verification Token" });
-  }
-  let url = config.default.lde.url + '/verify';
-  let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${tokenValue}`;
-  // Send confirmation email
-  let mailOptions = {
-    to: `"${user.name} <${user.email}>"`,
-    subject: "Confirme seu cadastro - Laboratório de Dados Educacionais",
-    text
-  }
-  email(mailOptions, (err, info) => {
-    if (err) {
-      log.error(err);
-      res.json({ msg: 'Message not delivered, user created but not confirmed' });
-    }
-    if (info) {
-      log.info(`Message ${info.messageId} sent: ${info.response}`);
-      log.info(`Usuário ${user.email} foi criado`);
-    }
-    res.json({ msg: 'User created' });
-  });
+  console.log(pb);
+  next();
 });
 
 pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
@@ -173,8 +131,8 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (r
   if ((req.body.password) && (req.body.newpassword)) {
     if (req.body.password != req.body.newpassword) {
       if (user.checkPassword(user, req.body.password)) {
-          await user.update({password:req.body.newpassword});
-        } 
+        await user.update({ password: req.body.newpassword });
+      }
       else {
         res.statusCode = 500;
         return res.json({
@@ -197,7 +155,8 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (r
     if (err) {
       log.error(err);
       return next({ message: 'Erro ao atualizar usuário' });
-    }})
+    }
+  })
   let u = user.toJSON();
   delete u.hashed_password;
   delete u.salt;
-- 
GitLab


From 07be2051441a3cea3d3d99625dd3044d54e3c00d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Wed, 31 May 2023 11:36:17 -0300
Subject: [PATCH 032/123] update classroomcount v2

---
 src/libs/routes_v2/classroomCount.js | 25 ++++++-------------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index fbac836a..b7a0ec7e 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -66,21 +66,6 @@ rqf.addField({
         foreign: 'municipio_id',
         foreignTable: '@'
     }
-}, 'dims').addValueToField({
-    name: 'cityTeacher',
-    table: 'municipio',
-    tableField: ['nome', 'id'],
-    resultField: ['city_name', 'city_id'],
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'id'
-    },
-    join: {
-        primary: 'id',
-        foreign: 'escola_cidade_id',
-        foreignTable: '@'
-    }
 }, 'dims').addValueToField({
     name: 'state',
     table: 'estado',
@@ -354,7 +339,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         req.teacherCalc = true;
     }
 
-    req.dims.cityTeacher = true;
+    req.dims.city = true;
     req.dims.formation_level = true;
     req.sql.field('SUM(simcaq_docente_agregada.num_docentes)', 'total')
            .field("'Brasil'", 'name')
@@ -530,11 +515,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 currentClassroomObj = obj;
 
                 var id_attribute = req.dims.school ? "school_id" : "city_id"
-                
                 var old_ti = ti;
-                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) // match da tabela de professores.
+                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) { // match da tabela de professores.
                     ti++;
-                
+                }
+
                 if (ti === req.teacher.length) {
                     while (classroom[id_attribute] === enrollments[j][id_attribute])
                         enrollments.splice(j, 1)
@@ -1137,6 +1122,8 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         })
     }
 
+    console.log('fim')
+
     next();
 }, response('classroom_count'));
 
-- 
GitLab


From 7fd2579bd3e2e1611a67f24ef5f9cf2b3aee2a4d Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 31 May 2023 11:45:35 -0300
Subject: [PATCH 033/123] [ADD] Routes

---
 src/libs/models/publication.js    |   2 +-
 src/libs/routes_v1/publication.js | 160 +++++++++++-------------------
 2 files changed, 57 insertions(+), 105 deletions(-)

diff --git a/src/libs/models/publication.js b/src/libs/models/publication.js
index ec3ed5c0..5b324e72 100644
--- a/src/libs/models/publication.js
+++ b/src/libs/models/publication.js
@@ -53,7 +53,7 @@ var Publication = db.define("Publication",{
     is_headline:{
         type:Sequelize.BOOLEAN,
         allowNull: false,
-        defaultValue: true
+        defaultValue: false
     }
 
 },
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index dcb02ce5..2a439104 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -76,6 +76,7 @@ pubApp.post('/', passport.authenticate('bearer', { session: false }), async (req
     text: req.body.texto,
     link: req.body.link,
     upload: req.body.upload,
+    is_draft: false,
     is_homepage: req.body.homepage
   }).catch(function (err) {
     log.error(err);
@@ -88,130 +89,81 @@ pubApp.post('/', passport.authenticate('bearer', { session: false }), async (req
     return res.json({ err, errors });
     // handle error;
   });
-  console.log(pb);
+  let up = await Publication.create({
+    user_id: req.user.id,
+    publication_id: pb.id
+  })
+  req.result = pb.toJSON();
+  next();
+});
+
+pubApp.post('/rascunho', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  let pb = await Publication.create({
+    id: 0,
+    filter: req.body.categoria,
+    title: req.body.title,
+    authors: req.body.autores,
+    organization: req.body.organizacao,
+    year: req.body.ano,
+    text: req.body.texto,
+    link: req.body.link,
+    upload: req.body.upload,
+    is_draft: true,
+    is_homepage: req.body.homepage
+  }).catch(function (err) {
+    log.error(err);
+    let errors = [];
+    for (let errName in err.errors) {
+      errors.push(err.errors[errName].message);
+    }
+    log.error(errors);
+    res.statusCode = 400;
+    return res.json({ err, errors });
+    // handle error;
+  });
+  let up = await Publication.create({
+    user_id: req.user.id,
+    publication_id: pb.id
+  })
+  req.result = pb.toJSON();
+  
   next();
 });
 
 pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-  let user = await User.findByPk(req.params.id).catch(function (err) {
+  let pb = await Publication.findByPk(req.params.id).catch(function (err) {
     if (err) {
       log.error(err);
       return next({ err });
     }
   })
-  if (!user) {
+  if (!pb) {
     res.statusCode = 404;
     return next({
       err: {
-        message: 'Usuário não encontrado'
+        message: 'Publicação não encontrada'
       }
     });
   }
-
-  user.email = req.body.email || user.email;
-  user.name = req.body.name || user.name;
-  user.nickname = req.body.nickname || user.nickname || user.name;
-  user.cep = req.body.cep || user.cep;
-  user.complement = req.body.complement || user.complement;
-  user.address = req.body.address || user.address;
-  user.phone = req.body.phone || user.phone;
-  user.schooling = req.body.schooling || user.schooling;
-  user.course = req.body.course || user.course;
-  user.segment = req.body.segment || user.segment;
-  user.role = req.body.role || user.role;
-  user.institutionName = req.body.institutionName || user.institutionName;
-  user.state = req.body.state || user.state;
-  user.city = req.body.city || user.city;
-  user.receiveEmails = req.body.receiveEmails || user.receiveEmails;
-  user.citesegment = req.body.citesegment || user.citesegment;
-  user.citerole = req.body.citerole || user.citerole;
-
-
-
-  if ((req.body.password) && (req.body.newpassword)) {
-    if (req.body.password != req.body.newpassword) {
-      if (user.checkPassword(user, req.body.password)) {
-        await user.update({ password: req.body.newpassword });
-      }
-      else {
-        res.statusCode = 500;
-        return res.json({
-          error: {
-            message: 'A senha atual está incorreta'
-          }
-        });
-      }
-    } else {
-      res.statusCode = 500;
-      return res.json({
-        error: {
-          message: 'A nova senha é a mesma da senha atual'
-        }
-      });
-    }
-  }
-
-  user.save().catch(err => {
+  pb.filter = req.body.categoria || pb.filter;
+  pb.title = req.body.title || pb.title;
+  pb.authors = req.body.autores || pb.authors;
+  pb.organization= req.body.organizacao || pb.organization;
+  pb.year= req.body.ano || pb.year;
+  pb.text= req.body.texto || pb.text;
+  pb.link= req.body.link || pb.link;
+  pb.upload= req.body.upload || pb.upload;
+  pb.is_homepage= req.body.homepage || pb.is_homepage;
+
+  pb.save().catch(err => {
     if (err) {
       log.error(err);
-      return next({ message: 'Erro ao atualizar usuário' });
+      return next({ message: 'Erro ao atualizar publicacao' });
     }
   })
-  let u = user.toJSON();
-  delete u.hashed_password;
-  delete u.salt;
-  delete u.password;
-  res.json({ user: u });
-
-});
-
+  let p = p.toJSON();
+  res.json({ publication: p });
 
-pubApp.get('/reset/password', async (req, res, next) => {
-  let emailAddress = req.query.email;
-  let user = await User.findOne({ where: { email: emailAddress } }).catch(function (err) {
-    log.error(err);
-    let errors = [];
-    for (let errName in err.errors) {
-      errors.push(err.errors[errName].message);
-    }
-    log.error(errors);
-    res.statusCode = 400;
-    return res.json({ err, errors });
-    // handle error;
-  });
-  if (!user) {
-    res.statusCode = 404;
-    res.json({ msg: "O usuário não está cadastrado" });
-  }
-  else {
-    let tokenValue = uuid.v4();
-    const rt = await ResetToken.create({
-      user_id: user.id,
-      token: tokenValue,
-      reset: false
-    });
-    if (!rt) {
-      res.statusCode = 404;
-      return res.json({ msg: "Couldn't create Reset Password Token" });
-    }
-    let url = config.default.lde.url + '/reset-password';
-    let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${tokenValue}`;
-    let mailOptions = {
-      to: `"${user.name} <${user.email}>"`,
-      subject: "Redefinição de Senha - Laboratório de Dados Educacionais",
-      text
-    }
-    console.log(mailOptions);
-    email(mailOptions, (err, info) => {
-      if (err) {
-        console.log(err);
-        log.error(err);
-        res.json({ msg: 'Undelivered Reset Password Mail' });
-      }
-      log.info(`Message ${info.messageId} sent: ${info.response}`);
-      res.json({ msg: 'Reset Password Mail Successfully Delivered' });
-    });
-  }
 });
 
 module.exports = pubApp;
-- 
GitLab


From 8adeedb4a28b6e64dfa0894842247914193c6ec5 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 1 Jun 2023 10:31:14 -0300
Subject: [PATCH 034/123] [FIX] Publication & ResetToken

---
 src/libs/routes_v1/publication.js | 14 ++++++++++----
 src/libs/routes_v1/resetToken.js  |  2 +-
 2 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 2a439104..46b0574d 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -8,14 +8,10 @@ const config = require(`${libs}/config`);
 
 const log = require(`${libs}/log`)(module);
 
-const VerificationToken = require(`${libs}/models/verificationToken`);
-
 const Publication = require(`${libs}/models/publication`);
 
 const UserPublication = require(`${libs}/models/userPublication`);
 
-const ResetToken = require(`${libs}/models/resetToken`);
-
 const response = require(`${libs}/middlewares/response`);
 
 const email = require(`${libs}/middlewares/email`);
@@ -166,4 +162,14 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (r
 
 });
 
+pubApp.delete('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  await Publication.destroy({where:{id:req.params.id}}).catch(function (err) {
+    if (err) {
+        log.error(err);
+        return next({ err });
+    }
+});
+  return next({ msg: 'Publication Deleted', status: 200 });
+});
+
 module.exports = pubApp;
diff --git a/src/libs/routes_v1/resetToken.js b/src/libs/routes_v1/resetToken.js
index 7c758410..579bec0b 100644
--- a/src/libs/routes_v1/resetToken.js
+++ b/src/libs/routes_v1/resetToken.js
@@ -24,7 +24,7 @@ resetTokenApp.get('/:token', async (req, res, next) => {
     }
     if (rToken.hasExpired()) {
         res.statusCode = 410;
-        await ResetToken.remove({where:{ token: token}}).catch(function (err) {
+        await ResetToken.destroy({where:{ token: token}}).catch(function (err) {
             if (err) {
                 log.error(err);
                 return next({ err });
-- 
GitLab


From 8630790d7552c22f08cb5444531588271af9b4a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 2 Jun 2023 11:02:59 -0300
Subject: [PATCH 035/123] rewrite queries for classroom_count

---
 src/libs/routes_v2/classroomCount.js | 94 +++++++++++++++++++++++++---
 src/libs/routes_v2/infrastructure.js | 29 +++++----
 2 files changed, 102 insertions(+), 21 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index b7a0ec7e..4e5c0dbd 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -228,9 +228,32 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
 
     req.dims.state = true;
     req.dims.city = true;
-    req.dims.school_year = true;
+    //req.dims.school_year = true;
     req.dims.location = true;
 
+    /*
+    select
+    sum(dia_total),
+    sum(noite_total),
+    'Brasil' as name,
+    ano_censo,
+    serie_ano_id,
+    estado_id,
+    municipio_id,
+    localizacao_id
+    from
+    simcaq_matricula_por_localizacao
+    where
+    serie_ano_id < 15
+    group by
+    name,
+    ano_censo,
+    serie_ano_id,
+    estado_id,
+    municipio_id,
+    localizacao_id
+    */
+
     req.sql.field('sum(dia_total)', 'total_day')
     .field('sum(noite_total)', 'total_night')
     .field("'Brasil'", 'name')
@@ -277,19 +300,42 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     next();
 }, rqf.parse(), (req, res, next) => {
 
+    /*
+    select
+    sum(escola_agregada.num_salas_utilizadas) as soma,
+    'Brasil' as name,
+    ano_censo,
+    estado_id,
+    municipio_id,
+    localizacao_id,
+    local_func_predio_escolar
+    from escola_agregada
+    where
+    situacao_de_funcionamento = 1 and
+    dependencia_adm_id IN (2, 3) and
+    escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1
+    group by
+    name,
+    ano_censo,
+    estado_id,
+    municipio_id,
+    localizacao_id,
+    local_func_predio_escolar
+    */
+
     req.dims.state = true;
     req.dims.city = true;
     req.dims.location = true;
     req.dims.school_building = true;
-
-    req.sql.field('SUM(escola_agregada.qtde_salas_utilizadas_dentro)', 'total')
+    
+    req.sql.field('SUM(escola_agregada.num_salas_utilizadas)', 'total')
     .field("'Brasil'", 'name')
     .field('escola_agregada.ano_censo', 'year')
     .from('escola_agregada')
     .group('escola_agregada.ano_censo')
     .order('escola_agregada.ano_censo')
-    .where('escola_agregada.situacao_funcionamento_pareada = 1')
-    .where('escola_agregada.dependencia_adm_id < 4') 
+    .where('escola_agregada.situacao_de_funcionamento = 1')
+    .where('escola_agregada.dependencia_adm_id IN (2,3)') 
     .where('escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1');
 
     next();
@@ -339,11 +385,22 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         req.teacherCalc = true;
     }
 
-    req.dims.city = true;
-    req.dims.formation_level = true;
-    req.sql.field('SUM(simcaq_docente_agregada.num_docentes)', 'total')
+    /*
+    select num_docentes,
+    'Brasil' as name,
+    ano_censo,
+    escola_estado_id,
+    municipio_id
+    from simcaq_docente_agregada
+    */
+
+    //req.dims.city = true;
+    //req.dims.state = true;
+    req.sql.field('simcaq_docente_agregada.num_docentes', 'total')
            .field("'Brasil'", 'name')
            .field('simcaq_docente_agregada.ano_censo', 'year')
+           .field('escola_estado_id', 'state_id')
+           .field('municipio_id', 'city_id')
            .from('simcaq_docente_agregada')
            .group('simcaq_docente_agregada.ano_censo')
            .order('simcaq_docente_agregada.ano_censo');
@@ -357,16 +414,33 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.resetSql();
     next();
 }, rqf.parse(), (req, res, next) => {
+    /*
+    select
+    SUM(num_matriculas) as num_matriculas,
+    ano_censo,
+    estado_id,
+    municipio_id,
+    etapa_resumida,
+    tempo_integral
+    from simcaq_matricula_agregada
+    group by
+    ano_censo,
+    estado_id,
+    municipio_id,
+    etapa_resumida,
+    tempo_integral
+    */
     req.dims.state = true;
     req.dims.city = true;
     req.dims.education_level_short = true;
     //req.dims.integral_time = true;
-    req.sql.field('COUNT(*)', 'total')
+    req.sql.field('SUM(num_matriculas)', 'total')
            .field('simcaq_matricula_agregada.ano_censo', 'year')
+           .field('simcaq_matricula_agregada.tempo_integral', 'integral_time')
            .from('simcaq_matricula_agregada')
            .group('simcaq_matricula_agregada.ano_censo')
+           .group('simcaq_matricula_agregada.tempo_integral')
            .order('simcaq_matricula_agregada.ano_censo');
-           //.where('((simcaq_matricula_agregada.tipo<=3 OR simcaq_matricula_agregada.tipo IS NULL) AND (simcaq_matricula_agregada.tipo_atendimento_turma IS NULL OR simcaq_matricula_agregada.tipo_atendimento_turma <= 2) AND simcaq_matricula_agregada.turma_turno_id <> 99)');
     next();
 }, rqf.build() ,query, id2str.transform(), (req, res, next) => {
     // constrói objeto de tempo integral e calcula diagnósticos
diff --git a/src/libs/routes_v2/infrastructure.js b/src/libs/routes_v2/infrastructure.js
index cc4c5931..01cc9264 100644
--- a/src/libs/routes_v2/infrastructure.js
+++ b/src/libs/routes_v2/infrastructure.js
@@ -335,7 +335,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     // Laboratório de informática
     let allInfLab = allSchools.clone();
     allInfLab.where('escola_agregada.func_predio_escolar = 1')
-    .where('escola_agregada.reg_fund_ai = 1 OR escola_agregada.reg_fund_af = 1 OR escola_agregada.reg_medio_medio = 1 OR escola_agregada.reg_medio_integrado = 1 OR escola_agregada.reg_medio_normal = 1 OR escola_agregada.ensino_eja_fund = 1 OR escola_agregada.ensino_eja_medio = 1 OR escola_agregada.ensino_eja_prof = 1');
+    .where('etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1');
     req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1;
 
     let haveInfLab = allInfLab.clone();
@@ -347,13 +347,13 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1;
 
     let haveScienceLab = allScienceLab.clone();
-    haveScienceLab.where('escola_agregada.lab_ciencias = 1');
+    haveScienceLab.where('escola_agregada.lab_ciencias = 1 AND (etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1;
 
     // Parque infantil
     let allKidsPark = allSchools.clone();
     allKidsPark.where('escola_agregada.func_predio_escolar = 1')
-    .where('escola_agregada.reg_infantil_creche = 1 OR escola_agregada.reg_infantil_preescola = 1 OR escola_agregada.reg_fund_ai = 1 OR escola_agregada.esp_infantil_creche = 1 OR escola_agregada.esp_exclusiva_creche = 1 OR escola_agregada.reg_esp_exclusiva_fund_ai = 1');
+    .where('escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1');
     req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1;
 
     let haveKidsPark = allKidsPark.clone();
@@ -372,27 +372,27 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
 
     // Quadra
     let allSportsCourt = allScienceLab.clone();
-    allSportsCourt.where('escola_agregada.localizacao_id = 1');
+    allSportsCourt.where('escola_agregada.localizacao_id = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1;
 
     let haveSportsCourt = allSportsCourt.clone();
-    haveSportsCourt.where('escola_agregada.quadra_esportes = 1');
+    haveSportsCourt.where('escola_agregada.quadra_esportes = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1;
 
     // Quadra coberta
     req.queryIndex.allCoveredSportsCourt = req.queryIndex.allSportsCourt;
 
     let haveCoveredSportsCourt = allSportsCourt.clone();
-    haveCoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 1');
+    haveCoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveCoveredSportsCourt = req.querySet.push(haveCoveredSportsCourt) - 1;
 
     // Quadra Descoberta
     let allUncoveredSportsCourt = allSportsCourt.clone();
-    allUncoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 0');
+    allUncoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 0 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.allUncoveredSportsCourt = req.querySet.push(allUncoveredSportsCourt) - 1;
 
     let haveUncoveredSportsCourt = allUncoveredSportsCourt.clone();
-    haveUncoveredSportsCourt.where('escola_agregada.quadra_esportes_descoberta = 1');
+    haveUncoveredSportsCourt.where('escola_agregada.quadra_esportes_descoberta = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveUncoveredSportsCourt = req.querySet.push(haveUncoveredSportsCourt) - 1;
 
     // Sala de direção
@@ -445,14 +445,14 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInternet = req.queryIndex.allLibrariesReadingRoom;
 
     let haveInternet = allLibrariesReadingRoom.clone();
-    haveInternet.where('escola_agregada.internet = 1');
+    haveInternet.where('escola_agregada.internet = 1 AND localizacao_id = 2');
     req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1;
 
     // Internet banda larga
     req.queryIndex.allBroadbandInternet = req.queryIndex.allLibraries;
 
     let haveBroadbandInternet = allLibraries.clone();
-    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1');
+    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1 AND localizacao_id = 1');
     req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1;
 
     // Banheiro dentro do prédio
@@ -466,7 +466,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInsideKidsBathroom = req.queryIndex.allKidsPark;
 
     let haveInsideKidsBathroom = allKidsPark.clone();
-    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1');
+    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1 AND (escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1) AND localizacao_id IN (1, 2)');
     req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1;
 
     // Fornecimento de energia
@@ -518,6 +518,13 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     haveAdaptedBuilding.where('escola_agregada.dependencias_pne = 1');
     req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1;
 
+    // Adicionar pátio
+    /*
+    patio = 1 ou 2 -- patio existe, 1 -- a ser coberto, 2 -- é coberto
+    func_predio_escolar = 1
+    localizacao_id = 1 ou 2
+    */
+
     next();
 }, multiQuery, (req, res, next) => {
     // Faz o matching entre os resultados
-- 
GitLab


From 522e92ece0bc9a47a6d6df8916063a8f3647623c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 5 Jun 2023 10:20:18 -0300
Subject: [PATCH 036/123] classroom count v2 working but does not return data

---
 src/libs/routes_v2/classroomCount.js | 41 +++++-----------------------
 1 file changed, 7 insertions(+), 34 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 4e5c0dbd..be2c8645 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -231,28 +231,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     //req.dims.school_year = true;
     req.dims.location = true;
 
-    /*
-    select
-    sum(dia_total),
-    sum(noite_total),
-    'Brasil' as name,
-    ano_censo,
-    serie_ano_id,
-    estado_id,
-    municipio_id,
-    localizacao_id
-    from
-    simcaq_matricula_por_localizacao
-    where
-    serie_ano_id < 15
-    group by
-    name,
-    ano_censo,
-    serie_ano_id,
-    estado_id,
-    municipio_id,
-    localizacao_id
-    */
+
 
     req.sql.field('sum(dia_total)', 'total_day')
     .field('sum(noite_total)', 'total_night')
@@ -386,21 +365,19 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     }
 
     /*
-    select num_docentes,
+    select sum(num_docentes),
     'Brasil' as name,
     ano_censo,
-    escola_estado_id,
+    estado_id,
     municipio_id
     from simcaq_docente_agregada
     */
 
-    //req.dims.city = true;
-    //req.dims.state = true;
-    req.sql.field('simcaq_docente_agregada.num_docentes', 'total')
+    req.dims.city = true;
+    req.dims.state = true;
+    req.sql.field('SUM(simcaq_docente_agregada.num_docentes)', 'total')
            .field("'Brasil'", 'name')
            .field('simcaq_docente_agregada.ano_censo', 'year')
-           .field('escola_estado_id', 'state_id')
-           .field('municipio_id', 'city_id')
            .from('simcaq_docente_agregada')
            .group('simcaq_docente_agregada.ano_censo')
            .order('simcaq_docente_agregada.ano_censo');
@@ -433,13 +410,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.dims.state = true;
     req.dims.city = true;
     req.dims.education_level_short = true;
-    //req.dims.integral_time = true;
+    req.dims.integral_time = true;
     req.sql.field('SUM(num_matriculas)', 'total')
            .field('simcaq_matricula_agregada.ano_censo', 'year')
-           .field('simcaq_matricula_agregada.tempo_integral', 'integral_time')
            .from('simcaq_matricula_agregada')
            .group('simcaq_matricula_agregada.ano_censo')
-           .group('simcaq_matricula_agregada.tempo_integral')
            .order('simcaq_matricula_agregada.ano_censo');
     next();
 }, rqf.build() ,query, id2str.transform(), (req, res, next) => {
@@ -1196,8 +1171,6 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         })
     }
 
-    console.log('fim')
-
     next();
 }, response('classroom_count'));
 
-- 
GitLab


From 8c4cc3ca5060fb51752c2a7daf95b970bd6ba690 Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Mon, 5 Jun 2023 10:28:57 -0300
Subject: [PATCH 037/123] trying to use the old version of classroom count

---
 src/libs/routes_v2/classroomCount.js | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index be2c8645..ea649bbf 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -228,7 +228,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
 
     req.dims.state = true;
     req.dims.city = true;
-    //req.dims.school_year = true;
+    req.dims.school_year = true;
     req.dims.location = true;
 
 
@@ -423,7 +423,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.integral_time = {}
     for (let i = 0; i < integral_time_result.length; ++i){
         // Se cidade não foi criada, cria
-        let integral_time = integral_time_result[i]
+        let integral_time = integral_time_result[i];
         let code = '' + integral_time.year + integral_time.city_id
         if (req.dims.school) code = code + integral_time.school_id
 
@@ -440,6 +440,8 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             if (req.dims.school){
                 obj.school_id = integral_time.school_id
                 obj.school_name = integral_time.school_name
+                obj.adm_dependency_id = integral_time.adm_dependency_id
+                obj.adm_dependency_name = integral_time.adm_dependency_name
             }
             req.integral_time[code] = obj
             currentCodeObj = obj;
@@ -525,7 +527,9 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             };
             if (req.dims.school){
                 obj.school_id = classroom.school_id,
-                obj.school_name = classroom.school_name
+                obj.school_name = classroom.school_name,
+                obj.adm_dependency_id = classroom.adm_dependency_id,
+                obj.adm_dependency_name = classroom.adm_dependency_name
             }
             if (req.teacherCalc)
                 obj.percentage_teacher_career = [];
@@ -564,12 +568,13 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 currentClassroomObj = obj;
 
                 var id_attribute = req.dims.school ? "school_id" : "city_id"
+                
                 var old_ti = ti;
-                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) { // match da tabela de professores.
+                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) // match da tabela de professores.
                     ti++;
-                }
-
+                
                 if (ti === req.teacher.length) {
+                    console.log(classroom[id_attribute], "not found")
                     while (classroom[id_attribute] === enrollments[j][id_attribute])
                         enrollments.splice(j, 1)
                     ti = old_ti; 
@@ -623,12 +628,13 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 let enrollmentEducationLevel = req.educationSchoolYear[enrollment.school_year_id];
                 // Se não há um número de alunos por turna para a etapa de ensino, ignoramos a entrada
 
-                if(typeof enrollmentEducationLevel.numberStudentClass == 'undefined' || enrollmentEducationLevel.numberStudentClass == null) continue;
+                if(enrollmentEducationLevel.numberStudentClass == null) continue;
                 
                 // Adiciona nível de educação para município/escola
                 let educationLevel = null;
                 if(!educationLevelSet.has(enrollmentEducationLevel.id)) { // cria e insere ordenadamente novo education level
                     educationLevelSet.add(enrollmentEducationLevel.id);
+                    
                     let itHash = '' + enrollment.year + enrollment.city_id
                     if (req.dims.school) itHash += enrollment.school_id
     
@@ -695,7 +701,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 let currentSchoolYear = null;
                 if(enrollmentEducationLevel.id == 1){
                     let schoolYearHash = '' + enrollment.year + enrollment.city_id + enrollment.location_id + enrollment.school_year_id;
-                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.school_id
+                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.shcool_id
 
                     if(schoolYearSet.has(schoolYearHash)) { // Busca a série escolar
                         let k = 0;
-- 
GitLab


From 03a18a3a3487436d690ace9c2a80c1aa5fc2a594 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 5 Jun 2023 11:36:46 -0300
Subject: [PATCH 038/123] [ADD] Integration with multer

---
 package.json                          |  1 +
 src/libs/middlewares/multer.config.js |  6 ++++
 src/libs/models/file.js               | 39 +++++++++++++++++++++++++
 src/libs/routes_v1/file.controller.js | 41 +++++++++++++++++++++++++++
 src/libs/routes_v1/publication.js     | 40 ++++++++++++++------------
 5 files changed, 109 insertions(+), 18 deletions(-)
 create mode 100644 src/libs/middlewares/multer.config.js
 create mode 100644 src/libs/models/file.js
 create mode 100644 src/libs/routes_v1/file.controller.js

diff --git a/package.json b/package.json
index 94ccf471..ee496631 100644
--- a/package.json
+++ b/package.json
@@ -52,6 +52,7 @@
     "mocha": "^3.5.3",
     "monetdb-pool": "0.0.8",
     "mongoose": "^4.13.17",
+    "multer": "^1.4.5-lts.1",
     "natives": "^1.1.6",
     "nconf": "^0.8.5",
     "node-uuid": "^1.4.8",
diff --git a/src/libs/middlewares/multer.config.js b/src/libs/middlewares/multer.config.js
new file mode 100644
index 00000000..d4c7263f
--- /dev/null
+++ b/src/libs/middlewares/multer.config.js
@@ -0,0 +1,6 @@
+const multer = require('multer');
+
+var storage = multer.memoryStorage()
+var upload = multer({storage: storage});
+
+module.exports = upload;
diff --git a/src/libs/models/file.js b/src/libs/models/file.js
new file mode 100644
index 00000000..aa6ba077
--- /dev/null
+++ b/src/libs/models/file.js
@@ -0,0 +1,39 @@
+
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
+const libs = `${process.cwd()}/libs`;
+
+var File = db.define("File",{
+    id: {
+        type: Sequelize.STRING,
+        allowNull:false,
+        unique: true,
+        primaryKey: true
+    },
+    type:{
+        type: Sequelize.STRING
+    },
+    name: {
+        type: Sequelize.STRING
+    },
+    data: {
+        type: Sequelize.BLOB('long')
+    }
+},
+{timestamps: false});
+
+File.generateObjectId = function(){
+    var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
+    return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function() {
+        return (Math.random() * 16 | 0).toString(16);
+    }).toLowerCase();
+}
+
+const setObjectId = file => {
+   file.id = File.generateObjectId()
+};
+
+File.beforeCreate(setObjectId);
+
+module.exports=File;
+	
diff --git a/src/libs/routes_v1/file.controller.js b/src/libs/routes_v1/file.controller.js
new file mode 100644
index 00000000..06464342
--- /dev/null
+++ b/src/libs/routes_v1/file.controller.js
@@ -0,0 +1,41 @@
+var stream = require('stream');
+const libs = `${process.cwd()}/libs`;
+const File = require(`${libs}/models/file`);
+
+exports.uploadFile = async (_file) => {
+	let file = await File.create({
+        id: 0,
+		type: _file.mimetype,
+		name: _file.originalname,
+		data: _file.buffer
+	}).catch(err => {
+		console.log(err);
+		res.json({msg: 'Error', detail: err});
+	});
+	return file.id;
+}
+
+exports.listAllFiles = (req, res) => {
+	File.findAll({attributes: ['id', 'name']}).then(files => {
+	  res.json(files);
+	}).catch(err => {
+		console.log(err);
+		res.json({msg: 'Error', detail: err});
+	});
+}
+
+exports.downloadFile = (req, res) => {
+	File.findByPk(req.params.id).then(file => {
+		var fileContents = Buffer.from(file.data, "base64");
+		var readStream = new stream.PassThrough();
+		readStream.end(fileContents);
+		
+		res.set('Content-disposition', 'attachment; filename=' + file.name);
+		res.set('Content-Type', file.type);
+
+		readStream.pipe(res);
+	}).catch(err => {
+		console.log(err);
+		res.json({msg: 'Error', detail: err});
+	});
+}
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 46b0574d..81341f32 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -18,7 +18,9 @@ const email = require(`${libs}/middlewares/email`);
 
 const passport = require('passport');
 
-const uuid = require('node-uuid');
+const fileWorker = require('./file.controller.js');
+
+let upload = require('../middlewares/multer.config.js');
 
 function emailSyntax(email) {
   const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
@@ -61,19 +63,26 @@ pubApp.get('/:id', (req, res, next) => {
   });
 }, response('publication'));
 
-pubApp.post('/', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+pubApp.post('/', upload.single('file'), async (req, res, next) => {
+  let _file_id = null
+  if(req.file){
+    _file_id = await fileWorker.uploadFile(req.file);
+    if(!_file_id)
+      console.log("NAO ARQUIVO");}
+  let data = JSON.parse(req.body.data);
+  console.log(data);
   let pb = await Publication.create({
     id: 0,
-    filter: req.body.categoria,
-    title: req.body.title,
-    authors: req.body.autores,
-    organization: req.body.organizacao,
-    year: req.body.ano,
-    text: req.body.texto,
-    link: req.body.link,
-    upload: req.body.upload,
+    filter: data.categoria,
+    title: data.title,
+    authors: data.autores,
+    organization: data.organizacao,
+    year: data.ano,
+    text: data.texto,
+    link: data.link,
+    upload: _file_id,
     is_draft: false,
-    is_homepage: req.body.homepage
+    is_homepage: data.homepage
   }).catch(function (err) {
     log.error(err);
     let errors = [];
@@ -85,13 +94,8 @@ pubApp.post('/', passport.authenticate('bearer', { session: false }), async (req
     return res.json({ err, errors });
     // handle error;
   });
-  let up = await Publication.create({
-    user_id: req.user.id,
-    publication_id: pb.id
-  })
-  req.result = pb.toJSON();
-  next();
-});
+  req.result = pb.toJSON;
+}, response('publication'));
 
 pubApp.post('/rascunho', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
   let pb = await Publication.create({
-- 
GitLab


From 4476d73afcd9a3dde83c4765aab5c0cfd4d6bab7 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 5 Jun 2023 11:51:07 -0300
Subject: [PATCH 039/123] [ADD] Rascunho

---
 src/libs/routes_v1/publication.js | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 81341f32..972928c7 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -70,7 +70,6 @@ pubApp.post('/', upload.single('file'), async (req, res, next) => {
     if(!_file_id)
       console.log("NAO ARQUIVO");}
   let data = JSON.parse(req.body.data);
-  console.log(data);
   let pb = await Publication.create({
     id: 0,
     filter: data.categoria,
@@ -97,19 +96,25 @@ pubApp.post('/', upload.single('file'), async (req, res, next) => {
   req.result = pb.toJSON;
 }, response('publication'));
 
-pubApp.post('/rascunho', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+pubApp.post('/rascunho', upload.single('file'), passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  let _file_id = null
+  if(req.file){
+    _file_id = await fileWorker.uploadFile(req.file);
+    if(!_file_id)
+      console.log("NAO ARQUIVO");}
+  let data = JSON.parse(req.body.data);
   let pb = await Publication.create({
     id: 0,
-    filter: req.body.categoria,
-    title: req.body.title,
-    authors: req.body.autores,
-    organization: req.body.organizacao,
-    year: req.body.ano,
-    text: req.body.texto,
-    link: req.body.link,
-    upload: req.body.upload,
+    filter: data.categoria,
+    title: data.title,
+    authors: data.autores,
+    organization: data.organizacao,
+    year: data.ano,
+    text: data.texto,
+    link: data.link,
+    upload: _file_id,
     is_draft: true,
-    is_homepage: req.body.homepage
+    is_homepage: data.homepage
   }).catch(function (err) {
     log.error(err);
     let errors = [];
@@ -128,7 +133,7 @@ pubApp.post('/rascunho', passport.authenticate('bearer', { session: false }), as
   req.result = pb.toJSON();
   
   next();
-});
+},response('rascunho'));
 
 pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
   let pb = await Publication.findByPk(req.params.id).catch(function (err) {
-- 
GitLab


From d127da953aa76802df74f53254a5035739b4b116 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 6 Jun 2023 11:52:12 -0300
Subject: [PATCH 040/123] update filters of school_infrastructure

---
 src/libs/routes_v2/infrastructure.js       |  6 -----
 src/libs/routes_v2/schoolInfrastructure.js | 28 +++++++++++-----------
 2 files changed, 14 insertions(+), 20 deletions(-)

diff --git a/src/libs/routes_v2/infrastructure.js b/src/libs/routes_v2/infrastructure.js
index 01cc9264..89d7e278 100644
--- a/src/libs/routes_v2/infrastructure.js
+++ b/src/libs/routes_v2/infrastructure.js
@@ -518,12 +518,6 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     haveAdaptedBuilding.where('escola_agregada.dependencias_pne = 1');
     req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1;
 
-    // Adicionar pátio
-    /*
-    patio = 1 ou 2 -- patio existe, 1 -- a ser coberto, 2 -- é coberto
-    func_predio_escolar = 1
-    localizacao_id = 1 ou 2
-    */
 
     next();
 }, multiQuery, (req, res, next) => {
diff --git a/src/libs/routes_v2/schoolInfrastructure.js b/src/libs/routes_v2/schoolInfrastructure.js
index da4d4593..00b87e03 100644
--- a/src/libs/routes_v2/schoolInfrastructure.js
+++ b/src/libs/routes_v2/schoolInfrastructure.js
@@ -387,7 +387,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     // ( reg_fund_ai_t1=1 | reg_fund_af_t1=1 | reg_medio_medio_t1=1 | ensino_eja_fund= 1 |     ensino_eja_medio= 1 | ensino_eja_prof= 1 | esp_eja_fund=1 | 
     // esp_eja_medio=1 | ensino_esp_exclusiva_eja_prof=1) então conta id
     let allInfLab = allSchools.clone();
-    allInfLab.where('reg_fund_ai_t1=1 OR reg_fund_af_t1=1 OR reg_medio_medio_t1=1 OR ensino_eja_fund=1 OR ensino_eja_medio=1 OR ensino_eja_prof=1 OR esp_eja_fund=1 OR esp_eja_medio=1 OR ensino_esp_exclusiva_eja_prof=1');
+    allInfLab.where('etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1');
     req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1;
 
     let haveInfLab = allInfLab.clone();
@@ -404,7 +404,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1;
 
     let haveScienceLab = allScienceLab.clone();
-    haveScienceLab.where('escola_agregada.lab_ciencias = true');
+    haveScienceLab.where('escola_agregada.lab_ciencias = 1 AND (etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1;
 
     let needScienceLab = allScienceLab.clone();
@@ -415,7 +415,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     // Se (situacao_de_funcionamento=1) and (ensino_regular=1 OR ensino_eja=1 OR educacao_profissional=1) and 
     // (local_func_predio_escolar=1) and (dependencia_adm_id<=3) and (reg_infantil_creche_t1=1 or reg_infantil_preescola_t1=1 or reg_fund_ai_t1=1) então conta id
     let allKidsPark = allSchools.clone();
-    allKidsPark.where('reg_infantil_creche_t1=1 OR reg_infantil_preescola_t1=1 OR reg_fund_ai_t1=1');
+    allKidsPark.where('escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1');
     req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1;
 
     let haveKidsPark = allKidsPark.clone();
@@ -441,15 +441,15 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
 
     // Quadra de esportes
     let allSportsCourt = allSchools.clone();
-    allSportsCourt.where('reg_fund_ai_t1=1  or reg_fund_af_t1=1 or  reg_medio_medio_t1=1  or  ensino_eja_fund= 1 or ensino_eja_medio= 1 or ensino_eja_prof= 1 or esp_eja_fund=1 or esp_eja_medio=1 or ensino_esp_exclusiva_eja_prof=1');
+    allSportsCourt.where('escola_agregada.localizacao_id = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1;
 
     let haveSportsCourt = allSportsCourt.clone();
-    haveSportsCourt.where('escola_agregada.quadra_esportes = 1');
+    haveSportsCourt.where('escola_agregada.quadra_esportes = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1;
 
     let needSportsCourt = allSportsCourt.clone();
-    needSportsCourt.where('escola_agregada.quadra_esportes = 0');
+    needSportsCourt.where('escola_agregada.quadra_esportes = 0 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.needSportsCourt = req.querySet.push(needSportsCourt) - 1;
 
     // Quadras a serem cobertas
@@ -468,11 +468,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allCourtyard = req.queryIndex.allSchools;
 
     let haveCourtyard = allSchools.clone();
-    haveCourtyard.where('escola_agregada.patio = 1 OR escola_agregada.patio = 2');
+    haveCourtyard.where('escola_agregada.func_predio_escolar = 1 AND (escola_agregada.patio = 1 OR escola_agregada.patio = 2)');
     req.queryIndex.haveCourtyard = req.querySet.push(haveCourtyard) - 1;
 
     let needCourtyard = allSchools.clone();
-    needCourtyard.where('escola_agregada.patio = 0');
+    needCourtyard.where('escola_agregada.func_predio_escolar = 1 AND escola_agregada.patio = 0');
     req.queryIndex.needCourtyard = req.querySet.push(needCourtyard) - 1;
 
     // Pátios a serem cobertos
@@ -554,11 +554,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInternet = req.queryIndex.allCountrySchools;
 
     let haveInternet = allCountrySchools.clone();
-    haveInternet.where('escola_agregada.internet = 1');
+    haveInternet.where('escola_agregada.internet = 1 AND localizacao_id = 2');
     req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1;
 
     let needInternet = allCountrySchools.clone();
-    needInternet.where('escola_agregada.internet = 0');
+    needInternet.where('escola_agregada.internet = 0 AND localizacao_id = 2');
     req.queryIndex.needInternet = req.querySet.push(needInternet) - 1;
 
     // Internet banda larga
@@ -567,11 +567,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allBroadbandInternet = req.queryIndex.allUrbanSchools;
 
     let haveBroadbandInternet = allUrbanSchools.clone();
-    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1');
+    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1 AND localizacao_id = 1');
     req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1;
 
     let needBroadbandInternet = allUrbanSchools.clone();
-    needBroadbandInternet.where('escola_agregada.internet_banda_larga = 0');
+    needBroadbandInternet.where('escola_agregada.internet_banda_larga = 0 AND localizacao_id = 1');
     req.queryIndex.needBroadbandInternet = req.querySet.push(needBroadbandInternet) - 1;
 
     // Banheiro
@@ -593,11 +593,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInsideKidsBathroom = req.querySet.push(allInsideKidsBathroom) - 1;
 
     let haveInsideKidsBathroom = allInsideKidsBathroom.clone();
-    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1');
+    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1 AND (escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1) AND localizacao_id IN (1, 2)');
     req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1;
 
     let needInsideKidsBathroom = allInsideKidsBathroom.clone();
-    needInsideKidsBathroom.where('escola_agregada.sanitario_ei = 0');
+    needInsideKidsBathroom.where('escola_agregada.sanitario_ei = 0 AND (escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1) AND localizacao_id IN (1, 2)');
     req.queryIndex.needInsideKidsBathroom = req.querySet.push(needInsideKidsBathroom) - 1;
 
     // Fornecimento de energia
-- 
GitLab


From a02836c622002bef16aac66b64892023b99d0382 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 7 Jun 2023 11:11:18 -0300
Subject: [PATCH 041/123] [ADD] GET PUBLICATIONS AND ACTIVITIES

---
 src/libs/models/activity.js       |  80 ++++++++++++++
 src/libs/models/userActivity.js   |  29 +++++
 src/libs/routes_v1/activity.js    | 176 ++++++++++++++++++++++++++++++
 src/libs/routes_v1/publication.js | 115 +++++++++----------
 4 files changed, 343 insertions(+), 57 deletions(-)
 create mode 100644 src/libs/models/activity.js
 create mode 100644 src/libs/models/userActivity.js
 create mode 100644 src/libs/routes_v1/activity.js

diff --git a/src/libs/models/activity.js b/src/libs/models/activity.js
new file mode 100644
index 00000000..f5a1cda2
--- /dev/null
+++ b/src/libs/models/activity.js
@@ -0,0 +1,80 @@
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
+const libs = `${process.cwd()}/libs`;
+
+var Activity = db.define("Activity",{
+    id:{
+        type: Sequelize.STRING,
+        allowNull:false,
+        unique: true,
+        primaryKey: true
+    },
+    type:{
+        type: Sequelize.STRING,
+        allowNull:false
+    },
+    title:{
+        type: Sequelize.STRING,
+        allowNull:false,
+    },
+    subtitle:{
+        type: Sequelize.STRING
+    },
+    date:{
+        type: Sequelize.DATE,
+        allowNull:false
+    },
+    authors:{
+        type: Sequelize.STRING,
+    },
+    text:{
+        type: Sequelize.STRING,
+        allowNull: false
+    },
+    name_headline:{
+        type: Sequelize.STRING,
+        allowNull:false
+    },
+    resume_headline:{
+        type:Sequelize.STRING,
+        allowNull:false
+    },
+    date_headline:{
+        type: Sequelize.DATE,
+        allowNull:false
+    },
+    local_headline:{
+        type:Sequelize.STRING,
+        allowNull:false
+    },
+    additional_headline:{
+        type:Sequelize.STRING
+    },
+    is_draft:{
+        type:Sequelize.BOOLEAN,
+        allowNull:false,
+        defaultValue: true
+    },
+    is_headline:{
+        type:Sequelize.BOOLEAN,
+        allowNull: false,
+        defaultValue: false
+    }
+
+},
+{timestamps: false});
+
+Activity.generateObjectId = function(){
+    var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
+    return timestamp + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function() {
+        return (Math.random() * 16 | 0).toString(16);
+    }).toLowerCase();
+}
+
+const setObjectId = act => {
+   act.id = Activity.generateObjectId()
+};
+
+Activity.beforeCreate(setObjectId);
+
+module.exports = Activity;
\ No newline at end of file
diff --git a/src/libs/models/userActivity.js b/src/libs/models/userActivity.js
new file mode 100644
index 00000000..2c1f8b56
--- /dev/null
+++ b/src/libs/models/userActivity.js
@@ -0,0 +1,29 @@
+const Sequelize = require("sequelize");
+const db = require('../db/postgres.js');
+const libs = `${process.cwd()}/libs`;
+const User = require(`${libs}/models/user`);
+const Activity = require(`${libs}/models/activity`);
+
+var userActivity = db.define("userActivity",{
+    id:{
+        type: Sequelize.INTEGER,
+        allowNull:false,
+        unique: true,
+        primaryKey: true
+    },
+    user_id:{
+        type: Sequelize.STRING,
+        allowNull:false,
+    },
+    activity_id:{
+        type: Sequelize.STRING,
+        allowNull:false
+    }
+},
+{timestamps: false});
+
+userActivity.hasMany(User, {foreignKey: 'id'});
+userActivity.hasMany(Activity, {foreignKey: 'id'});
+
+module.exports = userActivity;
+
diff --git a/src/libs/routes_v1/activity.js b/src/libs/routes_v1/activity.js
new file mode 100644
index 00000000..87ba5d1e
--- /dev/null
+++ b/src/libs/routes_v1/activity.js
@@ -0,0 +1,176 @@
+const express = require('express');
+
+const activityApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const config = require(`${libs}/config`);
+
+const log = require(`${libs}/log`)(module);
+
+const Activity = require(`${libs}/models/activity`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const email = require(`${libs}/middlewares/email`);
+
+const passport = require('passport');
+
+const fileWorker = require('./file.controller.js');
+
+let upload = require('../middlewares/multer.config.js');
+
+function emailSyntax(email) {
+  const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
+  return regex.test(email);
+}
+
+activityApp.get('/', async (req, res, next) => {
+  const page = parseInt(req.query.page) || 1; 
+  const pageSize = parseInt(req.query.pageSize) || 5; 
+  try {
+    const totalCount = await Activity.count();
+    const offset = (page - 1) * pageSize;
+
+    const acts = await Activity.findAll({
+      offset,
+      limit: pageSize,
+      where: {
+        is_draft: false
+      },
+      order:[
+        ['date', 'DESC']]
+    });
+
+    res.json({
+      page,
+      pageSize,
+      totalCount,
+      data: acts,
+    });
+  } catch (error) {
+    console.error(error);
+    res.status(500).json({ error: 'An error occurred' });
+  }
+});
+
+activityApp.get('/', async (req, res, next) => {
+    const page = parseInt(req.query.page) || 1; 
+    const pageSize = parseInt(req.query.pageSize) || 5; 
+    try {
+      const totalCount = await Activity.count();
+      const offset = (page - 1) * pageSize;
+  
+      const acts = await Activity.findAll({
+        offset,
+        limit: pageSize,
+        where: {
+          is_draft: acts
+        },
+        order:[
+            ['date', 'DESC']]
+      });
+  
+      res.json({
+        page,
+        pageSize,
+        totalCount,
+        data: acts,
+      });
+    } catch (error) {
+      console.error(error);
+      res.status(500).json({ error: 'An error occurred' });
+    }
+  });
+
+activityApp.get('/:id', (req, res, next) => {
+    Activity.findByPk(req.params.id).then((act) => {
+    if (!act) {
+      res.statusCode = 404;
+      res.json({ msg: "A atividade não está cadastrada" });
+    } else {
+      req.result = act.toJSON();
+      next();
+    }
+  }).catch(function (err) {
+    log.error(err);
+    return next(err);
+  });
+}, response('activity'));
+
+activityApp.post('/', async (req, res, next) => {
+  let data = JSON.parse(req.body.data);
+  let pb = await Publication.create({
+    id: 0,
+    filter: data.categoria,
+    title: data.title,
+    authors: data.autores,
+    organization: data.organizacao,
+    year: data.ano,
+    text: data.texto,
+    link: data.link,
+    upload: _file_id,
+    is_draft: data.rascunho,
+    is_homepage: data.homepage
+  }).catch(function (err) {
+    log.error(err);
+    let errors = [];
+    for (let errName in err.errors) {
+      errors.push(err.errors[errName].message);
+    }
+    log.error(errors);
+    res.statusCode = 400;
+    return res.json({ err, errors });
+    // handle error;
+  });
+  req.result = pb.toJSON();
+  next();
+}, response('publication'));
+
+activityApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  let pb = await Publication.findByPk(req.params.id).catch(function (err) {
+    if (err) {
+      log.error(err);
+      return next({ err });
+    }
+  })
+  if (!pb) {
+    res.statusCode = 404;
+    return next({
+      err: {
+        message: 'Publicação não encontrada'
+      }
+    });
+  }
+  pb.filter = req.body.categoria || pb.filter;
+  pb.title = req.body.title || pb.title;
+  pb.authors = req.body.autores || pb.authors;
+  pb.organization= req.body.organizacao || pb.organization;
+  pb.year= req.body.ano || pb.year;
+  pb.text= req.body.texto || pb.text;
+  pb.link= req.body.link || pb.link;
+  pb.upload= req.body.upload || pb.upload;
+  pb.is_homepage= req.body.homepage || pb.is_homepage;
+
+  pb.save().catch(err => {
+    if (err) {
+      log.error(err);
+      return next({ message: 'Erro ao atualizar publicacao' });
+    }
+  })
+  let p = p.toJSON();
+  res.json({ publication: p });
+
+});
+
+activityApp.delete('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  await Publication.destroy({where:{id:req.params.id}}).catch(function (err) {
+    if (err) {
+        log.error(err);
+        return next({ err });
+    }
+});
+  return next({ msg: 'Publication Deleted', status: 200 });
+});
+
+module.exports = activityApp;
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 972928c7..e29abd7e 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -27,26 +27,65 @@ function emailSyntax(email) {
   return regex.test(email);
 }
 
-pubApp.get('/', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-  let up = await UserPublication.findAll({ where: { user_id: req.user.id } }).catch(function (err) {
-    if (err) {
-      log.error(err);
-      return next(err);
-    }
-  });
-  console.log(up);
-  let publications = [];
-  for (let _id in up.publication_id) {
-    let pb = await Publication.findByPk(_id).catch(function (err) {
-      if (err) {
-        log.error(err);
-        return next(err);
+pubApp.get('/', async (req, res, next) => {
+  const page = parseInt(req.query.page) || 1; // Current page number
+  const pageSize = parseInt(req.query.pageSize) || 5; // Number of items per page
+  try {
+    const totalCount = await Publication.count();
+    const offset = (page - 1) * pageSize;
+
+    const publis = await Publication.findAll({
+      offset,
+      limit: pageSize,
+      where: {
+        is_draft: false
       }
     });
-    publications.push(pb);
+
+    res.json({
+      page,
+      pageSize,
+      totalCount,
+      data: publis,
+    });
+  } catch (error) {
+    console.error(error);
+    res.status(500).json({ error: 'An error occurred' });
   }
-  next();
-}, response('publications'));
+});
+
+pubApp.get('/drafts', async (req, res, next) => {
+  const page = parseInt(req.query.page) || 1; // Current page number
+  const pageSize = parseInt(req.query.pageSize) || 5; // Number of items per page
+
+  try {
+    // Count total number of items
+    const totalCount = await Publication.count();
+
+    // Calculate offset based on page and pageSize
+    const offset = (page - 1) * pageSize;
+
+    // Query the database with pagination options
+    const drafts = await Publication.findAll({
+      offset,
+      limit: pageSize,
+      where: {
+        is_draft: true
+      }
+    });
+
+    res.json({
+      page,
+      pageSize,
+      totalCount,
+      data: drafts,
+    });
+  } catch (error) {
+    console.error(error);
+    res.status(500).json({ error: 'An error occurred' });
+  }
+});
+
 
 pubApp.get('/:id', (req, res, next) => {
   Publication.findByPk(req.params.id).then((pb) => {
@@ -80,40 +119,7 @@ pubApp.post('/', upload.single('file'), async (req, res, next) => {
     text: data.texto,
     link: data.link,
     upload: _file_id,
-    is_draft: false,
-    is_homepage: data.homepage
-  }).catch(function (err) {
-    log.error(err);
-    let errors = [];
-    for (let errName in err.errors) {
-      errors.push(err.errors[errName].message);
-    }
-    log.error(errors);
-    res.statusCode = 400;
-    return res.json({ err, errors });
-    // handle error;
-  });
-  req.result = pb.toJSON;
-}, response('publication'));
-
-pubApp.post('/rascunho', upload.single('file'), passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-  let _file_id = null
-  if(req.file){
-    _file_id = await fileWorker.uploadFile(req.file);
-    if(!_file_id)
-      console.log("NAO ARQUIVO");}
-  let data = JSON.parse(req.body.data);
-  let pb = await Publication.create({
-    id: 0,
-    filter: data.categoria,
-    title: data.title,
-    authors: data.autores,
-    organization: data.organizacao,
-    year: data.ano,
-    text: data.texto,
-    link: data.link,
-    upload: _file_id,
-    is_draft: true,
+    is_draft: data.rascunho,
     is_homepage: data.homepage
   }).catch(function (err) {
     log.error(err);
@@ -126,14 +132,9 @@ pubApp.post('/rascunho', upload.single('file'), passport.authenticate('bearer',
     return res.json({ err, errors });
     // handle error;
   });
-  let up = await Publication.create({
-    user_id: req.user.id,
-    publication_id: pb.id
-  })
   req.result = pb.toJSON();
-  
   next();
-},response('rascunho'));
+}, response('publication'));
 
 pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
   let pb = await Publication.findByPk(req.params.id).catch(function (err) {
-- 
GitLab


From d0af2ab104f776600cbdfad2a65e72f57ca8256d Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 14 Jun 2023 10:22:30 -0300
Subject: [PATCH 042/123] [FIX] Api CheckPassword

---
 src/libs/middlewares/oauth2.js   |  5 ++-
 src/libs/middlewares/passport.js |  2 +-
 src/libs/routes_v1/activity.js   | 74 ++++++++++++++++++--------------
 src/libs/routes_v1/api.js        |  5 +++
 4 files changed, 51 insertions(+), 35 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 672c203e..cc9eaab0 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -54,14 +54,15 @@ let generateTokens = (userId, clientId, done) => {
 
 };
 
-
+// Exchange username & password for access token.
 aserver.exchange(oauth2orize.exchange.password(function(client, username, password, scope, done) {
     User.findOne({ 
       where: {email:username} 
     }).then(function(user) {
-      if(user == null){
+      if(user == null|| !user.checkPassword(password)){
         return done(null, false);
       }
+
       if(user.dataValues.origin != client.client_secret){
         console.log("Erro de client_secret");
         return done(null, false);
diff --git a/src/libs/middlewares/passport.js b/src/libs/middlewares/passport.js
index a63cd150..7e8d5120 100644
--- a/src/libs/middlewares/passport.js
+++ b/src/libs/middlewares/passport.js
@@ -20,7 +20,7 @@ passport.use(new ClientPasswordStrategy(
                 return done(null, false);
             }
             if (client.client_secret !== client_secret) {
-                console.log("Erro de geracao Chave Secreta");
+                console.log("Erro de Chave Secreta");
                 return done(null, false);
             }
             return done(null, client);
diff --git a/src/libs/routes_v1/activity.js b/src/libs/routes_v1/activity.js
index 87ba5d1e..9467021f 100644
--- a/src/libs/routes_v1/activity.js
+++ b/src/libs/routes_v1/activity.js
@@ -99,19 +99,22 @@ activityApp.get('/:id', (req, res, next) => {
 }, response('activity'));
 
 activityApp.post('/', async (req, res, next) => {
-  let data = JSON.parse(req.body.data);
-  let pb = await Publication.create({
+  console.log(req.body);
+  let act = await Activity.create({
     id: 0,
-    filter: data.categoria,
-    title: data.title,
-    authors: data.autores,
-    organization: data.organizacao,
-    year: data.ano,
-    text: data.texto,
-    link: data.link,
-    upload: _file_id,
-    is_draft: data.rascunho,
-    is_homepage: data.homepage
+    type:req.body.tipo,
+    title: req.body.titulo,
+    subtitle: req.body.subtitulo,
+    date: req.body.dataDePostagem,
+    authors:req.body.autor,
+    text: req.body.texto,
+    name_headline: req.body.nome,
+    resume_headline: req.body.resumo,
+    date_headline: req.body.dataAtividade,
+    local_headline: req.body.local,
+    additional_headline: req.body.informacoes,
+    is_draft: req.body.rascunho,
+    is_headline: req.body.is_headline
   }).catch(function (err) {
     log.error(err);
     let errors = [];
@@ -123,54 +126,61 @@ activityApp.post('/', async (req, res, next) => {
     return res.json({ err, errors });
     // handle error;
   });
-  req.result = pb.toJSON();
+  if(!act){
+    console.log("AA");
+  }
+  req.result = act.toJSON();
   next();
-}, response('publication'));
+}, response('activity'));
 
 activityApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-  let pb = await Publication.findByPk(req.params.id).catch(function (err) {
+  let act = await Activity.findByPk(req.params.id).catch(function (err) {
     if (err) {
       log.error(err);
       return next({ err });
     }
   })
-  if (!pb) {
+  if (!act) {
     res.statusCode = 404;
     return next({
       err: {
-        message: 'Publicação não encontrada'
+        message: 'Atividade não encontrada'
       }
     });
   }
-  pb.filter = req.body.categoria || pb.filter;
-  pb.title = req.body.title || pb.title;
-  pb.authors = req.body.autores || pb.authors;
-  pb.organization= req.body.organizacao || pb.organization;
-  pb.year= req.body.ano || pb.year;
-  pb.text= req.body.texto || pb.text;
-  pb.link= req.body.link || pb.link;
-  pb.upload= req.body.upload || pb.upload;
-  pb.is_homepage= req.body.homepage || pb.is_homepage;
-
-  pb.save().catch(err => {
+  act.type = req.body.type || act.type;
+  act.title = req.body.title || act.title;
+  act.subtitle = req.body.subtitle || act.subtitle;
+  act.date = req.body.date || act.date;
+  act.authors = req.body.autores || act.authors;
+  act.text= req.body.text || act.text;
+  act.name_headline= req.body.name_headline || act.name_headline;
+  act.resume_headline= req.body.resume_headline || act.resume_headline;
+  act.date_headline= req.body.date_headline || act.date_headline;
+  act.local_headline= req.body.local_headline || act.local_headline;
+  act.additional_headline= req.body.additional_headline || act.additional_headline;
+  act.is_draft= req.body.is_draft || act.is_draft;
+  act.is_headline= req.body.is_headline || act.is_headline;
+
+  act.save().catch(err => {
     if (err) {
       log.error(err);
       return next({ message: 'Erro ao atualizar publicacao' });
     }
   })
-  let p = p.toJSON();
-  res.json({ publication: p });
+  let activity = act.toJSON();
+  res.json({ activity: activity});
 
 });
 
 activityApp.delete('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
-  await Publication.destroy({where:{id:req.params.id}}).catch(function (err) {
+  await Activity.destroy({where:{id:req.params.id}}).catch(function (err) {
     if (err) {
         log.error(err);
         return next({ err });
     }
 });
-  return next({ msg: 'Publication Deleted', status: 200 });
+  return next({ msg: 'Activity Deleted', status: 200 });
 });
 
 module.exports = activityApp;
diff --git a/src/libs/routes_v1/api.js b/src/libs/routes_v1/api.js
index 50941a94..75a8e663 100644
--- a/src/libs/routes_v1/api.js
+++ b/src/libs/routes_v1/api.js
@@ -139,6 +139,8 @@ const courseStudents = require(`${libs}/routes_v1/courseStudents`);
 
 const publication = require(`${libs}/routes_v1/publication`);
 
+const activity = require(`${libs}/routes_v1/activity`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v1 is running' });
 });
@@ -202,4 +204,7 @@ api.use('/course_students', courseStudents);
 //Publication 
 api.use('/publication', publication);
 
+//Activity
+api.use('/activity', activity);
+
 module.exports = api;
-- 
GitLab


From a2f44a70b4d1b0768b5f2bef3d83c762220a622d Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 15 Jun 2023 11:10:19 -0300
Subject: [PATCH 043/123] [ADD] RETURN RESPONSE

---
 src/libs/middlewares/oauth2.js | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index cc9eaab0..2c85f516 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -23,10 +23,11 @@ let errFn = (cb, err) => {
 }
 
 // Destroys any old tokens and generates a new access and refresh token
-let generateTokens = (userId, clientId, done) => {
+let generateTokens = (userId, clientId,userRole, done) => {
     // curries in `done` callback so we don't need to pass it
     let refreshTokenValue;
     let tokenValue;
+    let admin = false; 
 
     RefreshToken.destroy({where:{"user_id": userId, "client_id": clientId}});
     AccessToken.destroy({where:{"user_id": userId, "client_id": clientId}});
@@ -47,19 +48,22 @@ let generateTokens = (userId, clientId, done) => {
         client_id:clientId,
         token:refreshed_token
     })
-
-    done(null, tokenValue, refreshTokenValue, {
+    if(userRole == 1){
+        admin = true;
+    }
+    done(null, tokenValue, refreshTokenValue, {'admin': admin},{
         'expires_in': config.security.tokenLife
     });
 
 };
 
+
 // Exchange username & password for access token.
 aserver.exchange(oauth2orize.exchange.password(function(client, username, password, scope, done) {
     User.findOne({ 
       where: {email:username} 
     }).then(function(user) {
-      if(user == null|| !user.checkPassword(password)){
+      if(user == null|| !user.checkPassword(user, password)){
         return done(null, false);
       }
 
@@ -68,7 +72,7 @@ aserver.exchange(oauth2orize.exchange.password(function(client, username, passwo
         return done(null, false);
       }
       log.info(`Gerando token para usuário ${user.name}`);
-      generateTokens(user.dataValues.id, client.id, done);
+      generateTokens(user.dataValues.id, client.id, user.dataValues.role_id, done);
     }).catch(function(error) {
       return done(error);
     });
@@ -107,4 +111,4 @@ exports.token = [
     passport.authenticate(['oauth2-client-password'], { session: false }),
     aserver.token(),
     aserver.errorHandler()
-];
+];
\ No newline at end of file
-- 
GitLab


From b709d6c1d646cb00a7448640edbe33b0f9fe50fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 22 Jun 2023 09:18:15 -0300
Subject: [PATCH 044/123] Changes to simcaq infrastructure

---
 src/libs/convert/scholarDependency.js         | 76 ++++++++++++++++
 src/libs/middlewares/id2str.js                |  4 +-
 src/libs/routes_v2/api.js                     |  3 +
 .../routes_v2/simcaqSchoolInfrastructure.js   | 87 +++++++++++++++++++
 4 files changed, 169 insertions(+), 1 deletion(-)
 create mode 100644 src/libs/convert/scholarDependency.js
 create mode 100644 src/libs/routes_v2/simcaqSchoolInfrastructure.js

diff --git a/src/libs/convert/scholarDependency.js b/src/libs/convert/scholarDependency.js
new file mode 100644
index 00000000..25684b9b
--- /dev/null
+++ b/src/libs/convert/scholarDependency.js
@@ -0,0 +1,76 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+module.exports = function scholarDependency(id) {
+    switch (id) {
+        case 1:
+            return "Biblioteca";
+        case 2:
+            return "Sala de leitura";
+        case 3:
+            return "Laboratório de informática";
+        case 4:
+            return "Laboratório de ciências";
+        case 5:
+            return "Parque infantil";
+        case 6:
+            return "Quadra poliesportiva";
+        case 7:
+            return "Quadras a serem cobertas";
+        case 8:
+            return "Pátio";
+        case 9:
+            return "Pátios a serem cobertos";
+        case 10:
+            return "Sala de direção";
+        case 11:
+            return "Secretaria";
+        case 12:
+            return "Sala de professores";
+        case 13:
+            return "Cozinha";
+        case 14:
+            return "Despensa";
+        case 15:
+            return "Almoxarifado";
+        case 16:
+            return "Internet";
+        case 17:
+            return "Internet banda larga";
+        case 18:
+            return "Banheiro dentro do prédio";
+        case 19:
+            return "Banheiro adequado para educação infantil dentro do prédio";
+        case 20:
+            return "Fornecimento de energia";
+        case 21:
+            return "Abastecimento de água";
+        case 22:
+            return "Água filtrada";
+        case 23:
+            return "Coleta de esgoto";
+        case 24:
+            return "Dependências adaptadas para pessoas com deficiências";
+        case 25:
+            return "Banheiros adaptados para pessoas com deficiências";
+        default:
+            return "Dependência escolar desconhecida";
+    }
+}
\ No newline at end of file
diff --git a/src/libs/middlewares/id2str.js b/src/libs/middlewares/id2str.js
index 39d819aa..cb549d3d 100644
--- a/src/libs/middlewares/id2str.js
+++ b/src/libs/middlewares/id2str.js
@@ -100,6 +100,7 @@ const admDependencyPub = require(`${libs}/convert/admDependencyPub`);
 const supplyDimension = require(`${libs}/convert/supplyDimension`);
 const type = require(`${libs}/convert/type`);
 const level = require(`${libs}/convert/level`);
+const scholarDependency = require(`${libs}/convert/scholarDependency`);
 
 const ids = {
     gender_id: gender,
@@ -191,7 +192,8 @@ const ids = {
     shift_id: shift,
     supply_dimension_id: supplyDimension,
     type_id: type,
-    level_id: level
+    level_id: level,
+    scholar_dependency_id: scholarDependency
 };
 
 function transform(removeId=false) {
diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 5c00d3fe..a8e1e1b7 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -160,6 +160,8 @@ const simcaqAggregatedEnrollment = require(`${libs}/routes_v2/simcaqAggregatedEn
 
 const simcaqTeacherCityPlan = require(`${libs}/routes_v2/simcaqTeacherCityPlan`);
 
+const simcaqSchoolInfrastructure = require(`${libs}/routes_v2/simcaqSchoolInfrastructure`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -231,5 +233,6 @@ api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
 api.use('/simcaq_enrollment_projection', simcaqEnrollmentProjection);
 api.use('/simcaq_aggregated_enrollment', simcaqAggregatedEnrollment);
 api.use('/simcaq_teacher_city_plan', simcaqTeacherCityPlan);
+api.use('/simcaq_school_infrastructure', simcaqSchoolInfrastructure);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqSchoolInfrastructure.js b/src/libs/routes_v2/simcaqSchoolInfrastructure.js
new file mode 100644
index 00000000..f10022cb
--- /dev/null
+++ b/src/libs/routes_v2/simcaqSchoolInfrastructure.js
@@ -0,0 +1,87 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqSchoolInfrastructureApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqSchoolInfrastructureApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_school_infrastructure',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'scholar_dependency',
+    table: 'simcaq_school_infrastructure',
+    tableField: 'scholar_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'scholar_dependency_id'
+    }
+});
+
+simcaqSchoolInfrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_school_infrastructure')
+        .field('simcaq_school_infrastructure.ano_censo', 'year')
+        .field('simcaq_school_infrastructure.scholar_dependency_id', 'scholar_dependency_id')
+        .field('SUM(simcaq_school_infrastructure.total_schools)', 'total_schools')
+        .field('SUM(total_no_dependency)', 'total_schools_without_dependency')
+        .field('SUM(total_with_dependency)', 'total_schools_with_dependency')
+        .group('simcaq_school_infrastructure.ano_censo')
+        .group('simcaq_school_infrastructure.scholar_dependency_id');
+    next();
+}, query, id2str.transform(), response('simcaqSchoolInfrastructure'));
+
+module.exports = simcaqSchoolInfrastructureApp;
-- 
GitLab


From 0832a14c9d6bfeac10f740d70980f212c3a39a4d Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Thu, 22 Jun 2023 10:26:50 -0300
Subject: [PATCH 045/123] [ADD] ACL in project

---
 src/libs/middlewares/authorize.js | 39 +++++++++++++++++++++++++++++++
 src/libs/models/activity.js       |  4 ++--
 src/libs/models/permission.js     |  2 +-
 src/libs/models/role.js           |  6 ++++-
 src/libs/routes_v1/activity.js    | 31 +++++++++++++++++++-----
 src/libs/routes_v1/publication.js |  8 ++++---
 6 files changed, 77 insertions(+), 13 deletions(-)
 create mode 100644 src/libs/middlewares/authorize.js

diff --git a/src/libs/middlewares/authorize.js b/src/libs/middlewares/authorize.js
new file mode 100644
index 00000000..592c193b
--- /dev/null
+++ b/src/libs/middlewares/authorize.js
@@ -0,0 +1,39 @@
+const express = require('express');
+
+const pubApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const PermissionRole = require(`${libs}/models/permissionRole`)
+
+const mapPermission = {
+    'criar publicacao': '1',
+    'editar publicacao': '2',
+    'apagar publicacao': '3',
+    'criar atividade': '4',
+    'editar atividade': '5',
+    'apagar atividade': '6',
+}
+ 
+function authorized(permission) {
+  return async function (req, res, next) {
+    const permission_id = mapPermission[permission]
+    const userRole = req.user.role_id; // Assuming user role is stored in req.user.role
+    const hasPermission = await PermissionRole.findOne({where:{role_id:userRole, permission_id: permission_id}}).catch(function (err) {
+      if (err) {
+          log.error(err);
+          return next({ err });
+      }
+  });
+    if (hasPermission) {
+      // User has permission, proceed to next middleware or route handler
+      next();
+    } else {
+      // User does not have permission, return unauthorized response
+      res.status(403).json({ message: 'Unauthorized' });
+    }
+  };
+}
+
+module.exports = authorized
+
diff --git a/src/libs/models/activity.js b/src/libs/models/activity.js
index f5a1cda2..92fc6f08 100644
--- a/src/libs/models/activity.js
+++ b/src/libs/models/activity.js
@@ -21,7 +21,7 @@ var Activity = db.define("Activity",{
         type: Sequelize.STRING
     },
     date:{
-        type: Sequelize.DATE,
+        type: Sequelize.STRING,
         allowNull:false
     },
     authors:{
@@ -40,7 +40,7 @@ var Activity = db.define("Activity",{
         allowNull:false
     },
     date_headline:{
-        type: Sequelize.DATE,
+        type: Sequelize.STRING,
         allowNull:false
     },
     local_headline:{
diff --git a/src/libs/models/permission.js b/src/libs/models/permission.js
index 9d827d30..ba35c4ce 100644
--- a/src/libs/models/permission.js
+++ b/src/libs/models/permission.js
@@ -35,5 +35,5 @@ const setObjectId = permission => {
 Permission.beforeCreate(setObjectId);
 
 
-module.exports = PermissionRole;
+module.exports = Permission;
 
diff --git a/src/libs/models/role.js b/src/libs/models/role.js
index dc30e09e..d594b061 100644
--- a/src/libs/models/role.js
+++ b/src/libs/models/role.js
@@ -11,7 +11,11 @@ var Role = db.define("Role",{
     role_name:{
         type: Sequelize.STRING,
         allowNull:false,
-    }
+    },
+    // token:{
+    //     type: Sequelize.STRING,
+    //     allowNull:false,
+    // }
 },
 {timestamps: false});
 
diff --git a/src/libs/routes_v1/activity.js b/src/libs/routes_v1/activity.js
index 9467021f..4468af18 100644
--- a/src/libs/routes_v1/activity.js
+++ b/src/libs/routes_v1/activity.js
@@ -98,19 +98,37 @@ activityApp.get('/:id', (req, res, next) => {
   });
 }, response('activity'));
 
-activityApp.post('/', async (req, res, next) => {
+
+function transformDateFormat(dateString) {
+  // Split the date string into day, month, and year components
+  var parts = dateString.split('/');
+
+  // Extract day, month, and year values
+  var day = parts[0];
+  var month = parts[1];
+  var year = parts[2];
+
+  // Concatenate the components in "yyyy/mm/dd" format
+  var transformedDate = year + '/' + month + '/' + day;
+
+  return transformedDate;
+}
+
+
+
+activityApp.post('/', passport.authenticate('bearer', { session: false }), authorized('criar atividade'), async (req, res, next) => {
   console.log(req.body);
   let act = await Activity.create({
     id: 0,
     type:req.body.tipo,
     title: req.body.titulo,
     subtitle: req.body.subtitulo,
-    date: req.body.dataDePostagem,
+    date: transformDateFormat(req.body.dataDePostagem),
     authors:req.body.autor,
     text: req.body.texto,
     name_headline: req.body.nome,
     resume_headline: req.body.resumo,
-    date_headline: req.body.dataAtividade,
+    date_headline: transformDateFormat(req.body.dataAtividade),
     local_headline: req.body.local,
     additional_headline: req.body.informacoes,
     is_draft: req.body.rascunho,
@@ -127,13 +145,14 @@ activityApp.post('/', async (req, res, next) => {
     // handle error;
   });
   if(!act){
-    console.log("AA");
+    res.statusCode = 400;
+    return res;
   }
   req.result = act.toJSON();
   next();
 }, response('activity'));
 
-activityApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+activityApp.put('/:id', passport.authenticate('bearer', { session: false }), authorized('editar atividade'), async (req, res, next) => {
   let act = await Activity.findByPk(req.params.id).catch(function (err) {
     if (err) {
       log.error(err);
@@ -173,7 +192,7 @@ activityApp.put('/:id', passport.authenticate('bearer', { session: false }), asy
 
 });
 
-activityApp.delete('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+activityApp.delete('/:id', passport.authenticate('bearer', { session: false }), authorized('editar atividade'), async (req, res, next) => {
   await Activity.destroy({where:{id:req.params.id}}).catch(function (err) {
     if (err) {
         log.error(err);
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index e29abd7e..e1e3b0f6 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -22,6 +22,8 @@ const fileWorker = require('./file.controller.js');
 
 let upload = require('../middlewares/multer.config.js');
 
+const authorized = require(`${libs}/middlewares/authorize.js`);
+
 function emailSyntax(email) {
   const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
   return regex.test(email);
@@ -102,7 +104,7 @@ pubApp.get('/:id', (req, res, next) => {
   });
 }, response('publication'));
 
-pubApp.post('/', upload.single('file'), async (req, res, next) => {
+pubApp.post('/', passport.authenticate('bearer', { session: false }), authorized('criar publicacao'), upload.single('file'), async (req, res, next) => {
   let _file_id = null
   if(req.file){
     _file_id = await fileWorker.uploadFile(req.file);
@@ -136,7 +138,7 @@ pubApp.post('/', upload.single('file'), async (req, res, next) => {
   next();
 }, response('publication'));
 
-pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+pubApp.put('/:id', passport.authenticate('bearer', { session: false }), authorized('editar publicacao'), async (req, res, next) => {
   let pb = await Publication.findByPk(req.params.id).catch(function (err) {
     if (err) {
       log.error(err);
@@ -172,7 +174,7 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), async (r
 
 });
 
-pubApp.delete('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+pubApp.delete('/:id', passport.authenticate('bearer', { session: false }), authorized('apagar publicacao'), async (req, res, next) => {
   await Publication.destroy({where:{id:req.params.id}}).catch(function (err) {
     if (err) {
         log.error(err);
-- 
GitLab


From 69b469187a26c3b638dda93af9a1be6e26a85c62 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Tue, 27 Jun 2023 09:14:09 -0300
Subject: [PATCH 046/123] [ADD] Updates

---
 src/libs/routes_v1/activity.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/libs/routes_v1/activity.js b/src/libs/routes_v1/activity.js
index 4468af18..0c6e1d02 100644
--- a/src/libs/routes_v1/activity.js
+++ b/src/libs/routes_v1/activity.js
@@ -167,6 +167,7 @@ activityApp.put('/:id', passport.authenticate('bearer', { session: false }), aut
       }
     });
   }
+  console.log("TEste");
   act.type = req.body.type || act.type;
   act.title = req.body.title || act.title;
   act.subtitle = req.body.subtitle || act.subtitle;
-- 
GitLab


From 16fe166d8a0489566bfeb91dcf89b75a502544f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 29 Jun 2023 10:45:25 -0300
Subject: [PATCH 047/123] Fix issue with classroom count rounding

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

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index 0af32828..a14250f9 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -646,7 +646,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                         education_level_short_name: enrollmentEducationLevel.name,
                         enrollment: {
                             integral_percentage: req.dims.school ? level_diagnosis : Math.max(level_diagnosis, enrollmentEducationLevel.integralTimeOfferGoal),
-                            integral_time: req.dims.school ? integral_time : Math.round(integral_time_total * Math.max(level_diagnosis, enrollmentEducationLevel.integralTimeOfferGoal)/100),
+                            integral_time: req.dims.school ? integral_time : Math.round((integral_time_total * Math.max(level_diagnosis, enrollmentEducationLevel.integralTimeOfferGoal)/100) * 10) / 10,
                             integral_time_total: integral_time_total,
                             total_enrollment_day: 0,
                             total_enrollment_night: 0,
-- 
GitLab


From 63e43123356037773f2a5ca9a4ed53a064d7f216 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 29 Jun 2023 11:48:07 -0300
Subject: [PATCH 048/123] Fix issue with schools without teachers

---
 src/libs/routes_v1/classroomCount.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index a14250f9..0edb0863 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -1151,6 +1151,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
 
                     educationLevel.teacherNumber.careerLevels = [];
                     req.teacherFormation.forEach((formation, i) => {
+                        if (educationLevel.teacherNumber.total_teacher < 1) {
+                            educationLevel.teacherNumber.total_teacher_full_period = 1;
+                            educationLevel.teacherNumber.total_teacher_partial = 1;
+                        }
+
                         let totalTeacherFullPeriodCareer = educationLevel.teacherNumber.total_teacher_full_period * teacherByFormation[i];
                         let totalTeacherPartialCareer = educationLevel.teacherNumber.total_teacher_partial * teacherByFormation[i];
 
-- 
GitLab


From bf6c735daaad30a011b06d1771a24a99fc442912 Mon Sep 17 00:00:00 2001
From: ppc19 <ppc19@inf.ufpr.br>
Date: Wed, 30 Nov 2022 08:55:23 -0300
Subject: [PATCH 049/123] change require fs to require fs extra

---
 gulpfile.babel.js                 | 2 +-
 src/libs/routes/classroomCount.js | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/gulpfile.babel.js b/gulpfile.babel.js
index 32500420..c4f477cd 100644
--- a/gulpfile.babel.js
+++ b/gulpfile.babel.js
@@ -1,6 +1,6 @@
 require('babel-core/register');
 
-const fs = require('fs');
+const fs = require('fs-extra');
 
 const gulp = require('gulp');
 
diff --git a/src/libs/routes/classroomCount.js b/src/libs/routes/classroomCount.js
index 038c6e45..cb7af893 100644
--- a/src/libs/routes/classroomCount.js
+++ b/src/libs/routes/classroomCount.js
@@ -43,7 +43,7 @@ const cache = require('apicache').options({ debug: config.debug, statusCodes: {i
 
 let rqf = new ReqQueryFields();
 
-const fs = require('fs')
+const fs = require('fs-extra')
 
 rqf.addField({
     name: 'filter',
-- 
GitLab


From c536672e9e0e98720c1fbef2399c05856bb9f99b Mon Sep 17 00:00:00 2001
From: ppc19 <ppc19@inf.ufpr.br>
Date: Wed, 30 Nov 2022 09:01:12 -0300
Subject: [PATCH 050/123] remove require fs

---
 gulpfile.babel.js                 | 2 --
 src/libs/routes/classroomCount.js | 2 --
 2 files changed, 4 deletions(-)

diff --git a/gulpfile.babel.js b/gulpfile.babel.js
index c4f477cd..c661d5af 100644
--- a/gulpfile.babel.js
+++ b/gulpfile.babel.js
@@ -1,7 +1,5 @@
 require('babel-core/register');
 
-const fs = require('fs-extra');
-
 const gulp = require('gulp');
 
 const babel = require('gulp-babel');
diff --git a/src/libs/routes/classroomCount.js b/src/libs/routes/classroomCount.js
index cb7af893..4f76bed3 100644
--- a/src/libs/routes/classroomCount.js
+++ b/src/libs/routes/classroomCount.js
@@ -43,8 +43,6 @@ const cache = require('apicache').options({ debug: config.debug, statusCodes: {i
 
 let rqf = new ReqQueryFields();
 
-const fs = require('fs-extra')
-
 rqf.addField({
     name: 'filter',
     field: false,
-- 
GitLab


From 2d041c223feac8289c4c2e7265b1b81d3fbade8b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 28 Feb 2023 11:19:31 -0300
Subject: [PATCH 051/123] Add testroute, path to testroute, Add new
 middleware's body parsing

---
 src/libs/middlewares/reqBody.js |  24 ++++
 src/libs/routes/api.js          |   4 +
 src/libs/routes/testroute.js    | 188 ++++++++++++++++++++++++++++++++
 3 files changed, 216 insertions(+)
 create mode 100644 src/libs/middlewares/reqBody.js
 create mode 100644 src/libs/routes/testroute.js

diff --git a/src/libs/middlewares/reqBody.js b/src/libs/middlewares/reqBody.js
new file mode 100644
index 00000000..e1f80cb1
--- /dev/null
+++ b/src/libs/middlewares/reqBody.js
@@ -0,0 +1,24 @@
+class ReqBody {
+    constructor() {
+
+    }
+    parse() {
+        return(req, res, next) => {
+            // Gets body of the HTTP requisition
+            let body = req.body;
+
+            // Chooses operation based on the mode field of the body
+            switch(body["mode"]) {
+                case "add_metrics":
+                    console.log(body);
+                    break;
+                default:
+                    break;
+            }
+
+            next();
+        }
+    }
+}
+
+module.exports = ReqBody;
\ No newline at end of file
diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js
index f6715eaa..1a68a1d4 100644
--- a/src/libs/routes/api.js
+++ b/src/libs/routes/api.js
@@ -134,6 +134,8 @@ const message = require(`${libs}/routes/message`);
 
 const courseStudents = require(`${libs}/routes/courseStudents`);
 
+const testRoute = require(`${libs}/routes/testroute`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API is running' });
 });
@@ -193,4 +195,6 @@ api.use('/universityLocalOffer', universityLocalOffer);
 api.use('/message', message);
 api.use('/course_students', courseStudents);
 
+api.use('/test', testRoute);
+
 module.exports = api;
diff --git a/src/libs/routes/testroute.js b/src/libs/routes/testroute.js
new file mode 100644
index 00000000..2f108eb7
--- /dev/null
+++ b/src/libs/routes/testroute.js
@@ -0,0 +1,188 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const testApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const ReqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+let reqBody = new ReqBody();
+
+testApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'min_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field:'ano_censo'
+    }
+}).addValue({
+    name: 'region',
+    table: 'escola',
+    tableField: 'regiao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'regiao_id'
+    }
+}).addValue({
+    name: 'region_not',
+    table: 'escola',
+    tableField: 'regiao_id',
+    where: {
+        relation: '<>',
+        type: 'integer',
+        field: 'regiao_id'
+    }
+}).addValue({
+    name: 'city',
+    table: 'escola',
+    tableField: 'municipio_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id'
+    }
+}).addValue({
+    name: 'city_not',
+    table: 'escola',
+    tableField: 'municipio_id',
+    where: {
+        relation: '<>',
+        type: 'integer',
+        field: 'municipio_id'
+    }
+}).addValue({
+    name: 'state',
+    table: 'escola',
+    tableField: 'estado_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id'
+    }
+}).addValue({
+    name: 'state_not',
+    table: 'escola',
+    tableField: 'estado_id',
+    where: {
+        relation: '<>',
+        type: 'integer',
+        field: 'estado_id'
+    }
+}).addValue({
+    name: 'dep_admin',
+    table: 'escola',
+    tableField: 'dependencia_adm_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'dep_pub_ins',
+    table: 'escola',
+    tableField: 'dependencia_convenio_publico',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_convenio_publico'
+    }
+}).addValue({
+    name: 'arrangement',
+    table: 'escola',
+    tableField: 'arranjo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'arranjo'
+    }
+}).addValue({
+    name: 'location',
+    table: 'escola',
+    tableField: 'localizacao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'diff_location',
+    table: 'escola',
+    tableField: 'localizacao_diferenciada_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_id'
+    }
+}).addValue({
+    name: 'full_time',
+    table: 'escola',
+    tableField: 'tempo_integral',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'tempo_integral'
+    }
+});
+
+testApp.get('/', rqf.parse(), rqf.build(), reqBody.parse(), (req, res, next) => {
+    req.sql.from('escola')
+        .field('ano_censo')
+        .field('count(*)', 'total')
+        .group('ano_censo').order('ano_censo')
+        .where('escola.situacao_funcionamento_pareada = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)')
+    console.log(req.sql.toString());
+    next();
+}, query, response('school'));
+
+module.exports = testApp;
\ No newline at end of file
-- 
GitLab


From d07a81ab116bb005562be8614596155e56c7b443 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Wed, 1 Mar 2023 11:29:52 -0300
Subject: [PATCH 052/123] Add functional version of middleware, add flag to
 check whether it runs default query or modified query

---
 src/libs/middlewares/reqBody.js | 37 ++++++++++++++++++++++++++++++++-
 src/libs/routes/testroute.js    | 21 +++++++++++++------
 2 files changed, 51 insertions(+), 7 deletions(-)

diff --git a/src/libs/middlewares/reqBody.js b/src/libs/middlewares/reqBody.js
index e1f80cb1..9df7ebca 100644
--- a/src/libs/middlewares/reqBody.js
+++ b/src/libs/middlewares/reqBody.js
@@ -2,6 +2,32 @@ class ReqBody {
     constructor() {
 
     }
+
+    add_metrics(req, column, metricsArray) {
+        for (let i in metricsArray) {
+            switch(metricsArray[i]["function"]) {
+                case "max":
+                    req.sql.field("max(" + column + ")", metricsArray[i]["return_name"]);
+                    req.hasMetrics = true;
+                    break;
+                case "min":
+                    req.sql.field("min(" + column + ")", metricsArray[i]["return_name"]);
+                    req.hasMetrics = true;
+                    break;
+                case "count":
+                    req.sql.field("count(" + column + ")", metricsArray[i]["return_name"]);
+                    req.hasMetrics = true;
+                    break;
+                case "sum":
+                    req.sql.field("sum(" + column + ")", metricsArray[i]["return_name"]);
+                    req.hasMetrics = true;
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
     parse() {
         return(req, res, next) => {
             // Gets body of the HTTP requisition
@@ -10,8 +36,17 @@ class ReqBody {
             // Chooses operation based on the mode field of the body
             switch(body["mode"]) {
                 case "add_metrics":
-                    console.log(body);
+                    // adds flag to check whether it runs the default query or not
+                    req.hasMetrics = false;
+                    // Gets all column names
+                    let columns = Object.keys(body["add_metrics"]);
+                    // Calls function to add metrics that were specified to req.sql
+                    for (let i in columns) {
+                        this.add_metrics(req, columns[i], body["add_metrics"][columns[i]]);
+                    }
+
                     break;
+
                 default:
                     break;
             }
diff --git a/src/libs/routes/testroute.js b/src/libs/routes/testroute.js
index 2f108eb7..97e28b90 100644
--- a/src/libs/routes/testroute.js
+++ b/src/libs/routes/testroute.js
@@ -176,12 +176,21 @@ rqf.addField({
 });
 
 testApp.get('/', rqf.parse(), rqf.build(), reqBody.parse(), (req, res, next) => {
-    req.sql.from('escola')
-        .field('ano_censo')
-        .field('count(*)', 'total')
-        .group('ano_censo').order('ano_censo')
-        .where('escola.situacao_funcionamento_pareada = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)')
-    console.log(req.sql.toString());
+    // Runs default query
+    if (!req.hasMetrics) {
+        req.sql.from('escola')
+            .field('ano_censo')
+            .field('count(*)', 'total')
+            .group('ano_censo').order('ano_censo')
+            .where('escola.situacao_funcionamento_pareada = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)')
+    }
+    // Runs modified query
+    else {
+        req.sql.from('escola')
+            .field('ano_censo')
+            .group('ano_censo').order('ano_censo')
+            .where('escola.situacao_funcionamento_pareada = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)')
+    }
     next();
 }, query, response('school'));
 
-- 
GitLab


From 791da624ca698a4bf857649e8157ba5f44d2c7f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 3 Mar 2023 09:29:20 -0300
Subject: [PATCH 053/123] Remove test route

---
 src/libs/routes/api.js       |   4 -
 src/libs/routes/testroute.js | 197 -----------------------------------
 2 files changed, 201 deletions(-)
 delete mode 100644 src/libs/routes/testroute.js

diff --git a/src/libs/routes/api.js b/src/libs/routes/api.js
index 1a68a1d4..f6715eaa 100644
--- a/src/libs/routes/api.js
+++ b/src/libs/routes/api.js
@@ -134,8 +134,6 @@ const message = require(`${libs}/routes/message`);
 
 const courseStudents = require(`${libs}/routes/courseStudents`);
 
-const testRoute = require(`${libs}/routes/testroute`);
-
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API is running' });
 });
@@ -195,6 +193,4 @@ api.use('/universityLocalOffer', universityLocalOffer);
 api.use('/message', message);
 api.use('/course_students', courseStudents);
 
-api.use('/test', testRoute);
-
 module.exports = api;
diff --git a/src/libs/routes/testroute.js b/src/libs/routes/testroute.js
deleted file mode 100644
index 97e28b90..00000000
--- a/src/libs/routes/testroute.js
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
-Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
-Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
-
-This file is part of simcaq-node.
-
-simcaq-node is free software: you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
-
-simcaq-node is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
-*/
-
-const express = require('express');
-
-const testApp = express.Router();
-
-const libs = `${process.cwd()}/libs`;
-
-const squel = require('squel');
-
-const query = require(`${libs}/middlewares/query`).query;
-
-const response = require(`${libs}/middlewares/response`);
-
-const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
-
-const ReqBody = require(`${libs}/middlewares/reqBody`);
-
-const config = require(`${libs}/config`); 
-
-const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
-
-let rqf = new ReqQueryFields();
-let reqBody = new ReqBody();
-
-testApp.use(cache('15 day'));
-
-rqf.addField({
-    name: 'filter',
-    field: false,
-    where: true
-}).addValue({
-    name: 'min_year',
-    table: 'escola',
-    tableField: 'ano_censo',
-    where: {
-        relation: '>=',
-        type: 'integer',
-        field: 'ano_censo'
-    }
-}).addValue({
-    name: 'max_year',
-    table: 'escola',
-    tableField: 'ano_censo',
-    where: {
-        relation: '<=',
-        type: 'integer',
-        field:'ano_censo'
-    }
-}).addValue({
-    name: 'region',
-    table: 'escola',
-    tableField: 'regiao_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'regiao_id'
-    }
-}).addValue({
-    name: 'region_not',
-    table: 'escola',
-    tableField: 'regiao_id',
-    where: {
-        relation: '<>',
-        type: 'integer',
-        field: 'regiao_id'
-    }
-}).addValue({
-    name: 'city',
-    table: 'escola',
-    tableField: 'municipio_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'municipio_id'
-    }
-}).addValue({
-    name: 'city_not',
-    table: 'escola',
-    tableField: 'municipio_id',
-    where: {
-        relation: '<>',
-        type: 'integer',
-        field: 'municipio_id'
-    }
-}).addValue({
-    name: 'state',
-    table: 'escola',
-    tableField: 'estado_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'estado_id'
-    }
-}).addValue({
-    name: 'state_not',
-    table: 'escola',
-    tableField: 'estado_id',
-    where: {
-        relation: '<>',
-        type: 'integer',
-        field: 'estado_id'
-    }
-}).addValue({
-    name: 'dep_admin',
-    table: 'escola',
-    tableField: 'dependencia_adm_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'dependencia_adm_id'
-    }
-}).addValue({
-    name: 'dep_pub_ins',
-    table: 'escola',
-    tableField: 'dependencia_convenio_publico',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'dependencia_convenio_publico'
-    }
-}).addValue({
-    name: 'arrangement',
-    table: 'escola',
-    tableField: 'arranjo',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'arranjo'
-    }
-}).addValue({
-    name: 'location',
-    table: 'escola',
-    tableField: 'localizacao_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'localizacao_id'
-    }
-}).addValue({
-    name: 'diff_location',
-    table: 'escola',
-    tableField: 'localizacao_diferenciada_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'localizacao_diferenciada_id'
-    }
-}).addValue({
-    name: 'full_time',
-    table: 'escola',
-    tableField: 'tempo_integral',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'tempo_integral'
-    }
-});
-
-testApp.get('/', rqf.parse(), rqf.build(), reqBody.parse(), (req, res, next) => {
-    // Runs default query
-    if (!req.hasMetrics) {
-        req.sql.from('escola')
-            .field('ano_censo')
-            .field('count(*)', 'total')
-            .group('ano_censo').order('ano_censo')
-            .where('escola.situacao_funcionamento_pareada = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)')
-    }
-    // Runs modified query
-    else {
-        req.sql.from('escola')
-            .field('ano_censo')
-            .group('ano_censo').order('ano_censo')
-            .where('escola.situacao_funcionamento_pareada = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)')
-    }
-    next();
-}, query, response('school'));
-
-module.exports = testApp;
\ No newline at end of file
-- 
GitLab


From f28d30f7883129cb164cb8033d6a3c8b0202bcc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 3 Mar 2023 11:09:46 -0300
Subject: [PATCH 054/123] Create v2 version, adapt app.js to use both versions

---
 src/libs/app.js                               |    8 +-
 src/libs/{routes => routes_v1}/api.js         |   70 +-
 src/libs/{routes => routes_v1}/auxiliar.js    |    0
 src/libs/{routes => routes_v1}/city.js        |    0
 src/libs/{routes => routes_v1}/class.js       |    0
 src/libs/{routes => routes_v1}/classCount.js  |    0
 src/libs/{routes => routes_v1}/classroom.js   |    0
 .../{routes => routes_v1}/classroomCount.js   |    0
 src/libs/{routes => routes_v1}/courseCount.js |    0
 .../{routes => routes_v1}/courseStudents.js   |    0
 src/libs/{routes => routes_v1}/cub.js         |    0
 .../dailyChargeAmount.js                      |    0
 src/libs/{routes => routes_v1}/disciplines.js |    0
 .../distributionFactor.js                     |    0
 src/libs/{routes => routes_v1}/downloads.js   |    0
 .../{routes => routes_v1}/educationYears.js   |    0
 .../educationalBudget.js                      |    0
 src/libs/{routes => routes_v1}/employees.js   |    0
 src/libs/{routes => routes_v1}/enrollment.js  |    0
 .../enrollmentProjection.js                   |    0
 src/libs/{routes => routes_v1}/financial.js   |    0
 .../glossEnrollmentRatio.js                   |    0
 src/libs/{routes => routes_v1}/idhm.js        |    0
 src/libs/{routes => routes_v1}/idhme.js       |    0
 src/libs/{routes => routes_v1}/idhml.js       |    0
 src/libs/{routes => routes_v1}/idhmr.js       |    0
 .../{routes => routes_v1}/infrastructure.js   |    0
 .../liquidEnrollmentRatio.js                  |    0
 src/libs/{routes => routes_v1}/location.js    |    0
 src/libs/{routes => routes_v1}/mesoregion.js  |    0
 src/libs/{routes => routes_v1}/message.js     |    0
 src/libs/{routes => routes_v1}/microregion.js |    0
 src/libs/{routes => routes_v1}/outOfSchool.js |    0
 .../{routes => routes_v1}/pibpercapita.js     |    0
 src/libs/{routes => routes_v1}/population.js  |    0
 src/libs/{routes => routes_v1}/portalMec.js   |    0
 .../{routes => routes_v1}/portalMecInep.js    |    0
 src/libs/{routes => routes_v1}/rateSchool.js  |    0
 src/libs/{routes => routes_v1}/region.js      |    0
 src/libs/{routes => routes_v1}/resetToken.js  |    0
 src/libs/routes_v1/school.js                  |  663 +++++++++
 .../schoolInfrastructure.js                   |    0
 .../{routes => routes_v1}/schoolLocation.js   |    0
 src/libs/{routes => routes_v1}/simulation.js  |    0
 src/libs/{routes => routes_v1}/siope.js       |    0
 src/libs/{routes => routes_v1}/spatial.js     |    0
 src/libs/{routes => routes_v1}/state.js       |    0
 src/libs/{routes => routes_v1}/studentsAee.js |    0
 src/libs/{routes => routes_v1}/teacher.js     |    0
 src/libs/{routes => routes_v1}/transport.js   |    0
 src/libs/{routes => routes_v1}/university.js  |    0
 .../universityEnrollment.js                   |    0
 .../universityLocalOffer.js                   |    0
 .../universityTeacher.js                      |    0
 src/libs/{routes => routes_v1}/user.js        |    0
 src/libs/{routes => routes_v1}/verifyToken.js |    0
 src/libs/routes_v2/api.js                     |  196 +++
 src/libs/routes_v2/auxiliar.js                |  397 ++++++
 src/libs/routes_v2/city.js                    |  151 +++
 src/libs/routes_v2/class.js                   |  402 ++++++
 src/libs/routes_v2/classCount.js              |  455 +++++++
 src/libs/routes_v2/classroom.js               |  277 ++++
 src/libs/routes_v2/classroomCount.js          | 1172 ++++++++++++++++
 src/libs/routes_v2/courseCount.js             |  814 +++++++++++
 src/libs/routes_v2/courseStudents.js          |  201 +++
 src/libs/routes_v2/cub.js                     |  227 ++++
 src/libs/routes_v2/dailyChargeAmount.js       |  467 +++++++
 src/libs/routes_v2/disciplines.js             |  682 ++++++++++
 src/libs/routes_v2/distributionFactor.js      |  210 +++
 src/libs/routes_v2/downloads.js               |   54 +
 src/libs/routes_v2/educationYears.js          |   46 +
 src/libs/routes_v2/educationalBudget.js       |  325 +++++
 src/libs/routes_v2/employees.js               |  656 +++++++++
 src/libs/routes_v2/enrollment.js              | 1204 +++++++++++++++++
 src/libs/routes_v2/enrollmentProjection.js    |  239 ++++
 src/libs/routes_v2/financial.js               |  186 +++
 src/libs/routes_v2/glossEnrollmentRatio.js    |  390 ++++++
 src/libs/routes_v2/idhm.js                    |  212 +++
 src/libs/routes_v2/idhme.js                   |  185 +++
 src/libs/routes_v2/idhml.js                   |  185 +++
 src/libs/routes_v2/idhmr.js                   |  188 +++
 src/libs/routes_v2/infrastructure.js          |  585 ++++++++
 src/libs/routes_v2/liquidEnrollmentRatio.js   |  434 ++++++
 src/libs/routes_v2/location.js                |  135 ++
 src/libs/routes_v2/mesoregion.js              |   86 ++
 src/libs/routes_v2/message.js                 |   55 +
 src/libs/routes_v2/microregion.js             |   99 ++
 src/libs/routes_v2/outOfSchool.js             |  377 ++++++
 src/libs/routes_v2/pibpercapita.js            |  250 ++++
 src/libs/routes_v2/population.js              |  163 +++
 src/libs/routes_v2/portalMec.js               |  136 ++
 src/libs/routes_v2/portalMecInep.js           |   60 +
 src/libs/routes_v2/rateSchool.js              |  337 +++++
 src/libs/routes_v2/region.js                  |   72 +
 src/libs/routes_v2/resetToken.js              |   81 ++
 src/libs/{routes => routes_v2}/school.js      |    0
 src/libs/routes_v2/schoolInfrastructure.js    |  741 ++++++++++
 src/libs/routes_v2/schoolLocation.js          |  114 ++
 src/libs/routes_v2/simulation.js              |  167 +++
 src/libs/routes_v2/siope.js                   |  186 +++
 src/libs/routes_v2/spatial.js                 |  373 +++++
 src/libs/routes_v2/state.js                   |  108 ++
 src/libs/routes_v2/studentsAee.js             |  219 +++
 src/libs/routes_v2/teacher.js                 |  576 ++++++++
 src/libs/routes_v2/transport.js               |  400 ++++++
 src/libs/routes_v2/university.js              |  347 +++++
 src/libs/routes_v2/universityEnrollment.js    |  848 ++++++++++++
 src/libs/routes_v2/universityLocalOffer.js    |  170 +++
 src/libs/routes_v2/universityTeacher.js       |  517 +++++++
 src/libs/routes_v2/user.js                    |  305 +++++
 src/libs/routes_v2/verifyToken.js             |   52 +
 111 files changed, 18218 insertions(+), 37 deletions(-)
 rename src/libs/{routes => routes_v1}/api.js (63%)
 rename src/libs/{routes => routes_v1}/auxiliar.js (100%)
 rename src/libs/{routes => routes_v1}/city.js (100%)
 rename src/libs/{routes => routes_v1}/class.js (100%)
 rename src/libs/{routes => routes_v1}/classCount.js (100%)
 rename src/libs/{routes => routes_v1}/classroom.js (100%)
 rename src/libs/{routes => routes_v1}/classroomCount.js (100%)
 rename src/libs/{routes => routes_v1}/courseCount.js (100%)
 rename src/libs/{routes => routes_v1}/courseStudents.js (100%)
 rename src/libs/{routes => routes_v1}/cub.js (100%)
 rename src/libs/{routes => routes_v1}/dailyChargeAmount.js (100%)
 rename src/libs/{routes => routes_v1}/disciplines.js (100%)
 rename src/libs/{routes => routes_v1}/distributionFactor.js (100%)
 rename src/libs/{routes => routes_v1}/downloads.js (100%)
 rename src/libs/{routes => routes_v1}/educationYears.js (100%)
 rename src/libs/{routes => routes_v1}/educationalBudget.js (100%)
 rename src/libs/{routes => routes_v1}/employees.js (100%)
 rename src/libs/{routes => routes_v1}/enrollment.js (100%)
 rename src/libs/{routes => routes_v1}/enrollmentProjection.js (100%)
 rename src/libs/{routes => routes_v1}/financial.js (100%)
 rename src/libs/{routes => routes_v1}/glossEnrollmentRatio.js (100%)
 rename src/libs/{routes => routes_v1}/idhm.js (100%)
 rename src/libs/{routes => routes_v1}/idhme.js (100%)
 rename src/libs/{routes => routes_v1}/idhml.js (100%)
 rename src/libs/{routes => routes_v1}/idhmr.js (100%)
 rename src/libs/{routes => routes_v1}/infrastructure.js (100%)
 rename src/libs/{routes => routes_v1}/liquidEnrollmentRatio.js (100%)
 rename src/libs/{routes => routes_v1}/location.js (100%)
 rename src/libs/{routes => routes_v1}/mesoregion.js (100%)
 rename src/libs/{routes => routes_v1}/message.js (100%)
 rename src/libs/{routes => routes_v1}/microregion.js (100%)
 rename src/libs/{routes => routes_v1}/outOfSchool.js (100%)
 rename src/libs/{routes => routes_v1}/pibpercapita.js (100%)
 rename src/libs/{routes => routes_v1}/population.js (100%)
 rename src/libs/{routes => routes_v1}/portalMec.js (100%)
 rename src/libs/{routes => routes_v1}/portalMecInep.js (100%)
 rename src/libs/{routes => routes_v1}/rateSchool.js (100%)
 rename src/libs/{routes => routes_v1}/region.js (100%)
 rename src/libs/{routes => routes_v1}/resetToken.js (100%)
 create mode 100644 src/libs/routes_v1/school.js
 rename src/libs/{routes => routes_v1}/schoolInfrastructure.js (100%)
 rename src/libs/{routes => routes_v1}/schoolLocation.js (100%)
 rename src/libs/{routes => routes_v1}/simulation.js (100%)
 rename src/libs/{routes => routes_v1}/siope.js (100%)
 rename src/libs/{routes => routes_v1}/spatial.js (100%)
 rename src/libs/{routes => routes_v1}/state.js (100%)
 rename src/libs/{routes => routes_v1}/studentsAee.js (100%)
 rename src/libs/{routes => routes_v1}/teacher.js (100%)
 rename src/libs/{routes => routes_v1}/transport.js (100%)
 rename src/libs/{routes => routes_v1}/university.js (100%)
 rename src/libs/{routes => routes_v1}/universityEnrollment.js (100%)
 rename src/libs/{routes => routes_v1}/universityLocalOffer.js (100%)
 rename src/libs/{routes => routes_v1}/universityTeacher.js (100%)
 rename src/libs/{routes => routes_v1}/user.js (100%)
 rename src/libs/{routes => routes_v1}/verifyToken.js (100%)
 create mode 100644 src/libs/routes_v2/api.js
 create mode 100644 src/libs/routes_v2/auxiliar.js
 create mode 100644 src/libs/routes_v2/city.js
 create mode 100644 src/libs/routes_v2/class.js
 create mode 100644 src/libs/routes_v2/classCount.js
 create mode 100644 src/libs/routes_v2/classroom.js
 create mode 100644 src/libs/routes_v2/classroomCount.js
 create mode 100644 src/libs/routes_v2/courseCount.js
 create mode 100644 src/libs/routes_v2/courseStudents.js
 create mode 100644 src/libs/routes_v2/cub.js
 create mode 100644 src/libs/routes_v2/dailyChargeAmount.js
 create mode 100644 src/libs/routes_v2/disciplines.js
 create mode 100644 src/libs/routes_v2/distributionFactor.js
 create mode 100644 src/libs/routes_v2/downloads.js
 create mode 100644 src/libs/routes_v2/educationYears.js
 create mode 100644 src/libs/routes_v2/educationalBudget.js
 create mode 100644 src/libs/routes_v2/employees.js
 create mode 100644 src/libs/routes_v2/enrollment.js
 create mode 100644 src/libs/routes_v2/enrollmentProjection.js
 create mode 100644 src/libs/routes_v2/financial.js
 create mode 100644 src/libs/routes_v2/glossEnrollmentRatio.js
 create mode 100644 src/libs/routes_v2/idhm.js
 create mode 100644 src/libs/routes_v2/idhme.js
 create mode 100644 src/libs/routes_v2/idhml.js
 create mode 100644 src/libs/routes_v2/idhmr.js
 create mode 100644 src/libs/routes_v2/infrastructure.js
 create mode 100644 src/libs/routes_v2/liquidEnrollmentRatio.js
 create mode 100644 src/libs/routes_v2/location.js
 create mode 100644 src/libs/routes_v2/mesoregion.js
 create mode 100644 src/libs/routes_v2/message.js
 create mode 100644 src/libs/routes_v2/microregion.js
 create mode 100644 src/libs/routes_v2/outOfSchool.js
 create mode 100644 src/libs/routes_v2/pibpercapita.js
 create mode 100644 src/libs/routes_v2/population.js
 create mode 100644 src/libs/routes_v2/portalMec.js
 create mode 100644 src/libs/routes_v2/portalMecInep.js
 create mode 100644 src/libs/routes_v2/rateSchool.js
 create mode 100644 src/libs/routes_v2/region.js
 create mode 100644 src/libs/routes_v2/resetToken.js
 rename src/libs/{routes => routes_v2}/school.js (100%)
 create mode 100644 src/libs/routes_v2/schoolInfrastructure.js
 create mode 100644 src/libs/routes_v2/schoolLocation.js
 create mode 100644 src/libs/routes_v2/simulation.js
 create mode 100644 src/libs/routes_v2/siope.js
 create mode 100644 src/libs/routes_v2/spatial.js
 create mode 100644 src/libs/routes_v2/state.js
 create mode 100644 src/libs/routes_v2/studentsAee.js
 create mode 100644 src/libs/routes_v2/teacher.js
 create mode 100644 src/libs/routes_v2/transport.js
 create mode 100644 src/libs/routes_v2/university.js
 create mode 100644 src/libs/routes_v2/universityEnrollment.js
 create mode 100644 src/libs/routes_v2/universityLocalOffer.js
 create mode 100644 src/libs/routes_v2/universityTeacher.js
 create mode 100644 src/libs/routes_v2/user.js
 create mode 100644 src/libs/routes_v2/verifyToken.js

diff --git a/src/libs/app.js b/src/libs/app.js
index b8b953c1..9f185105 100644
--- a/src/libs/app.js
+++ b/src/libs/app.js
@@ -14,7 +14,8 @@ const config = require(`${libs}/config`);
 
 const app = express();
 
-const api = require('./routes/api');
+const api_v1 = require('./routes_v1/api');
+const api_v2 = require('./routes_v2/api');
 
 const passport = require('passport');
 
@@ -65,7 +66,10 @@ app.use((req, res, next) => {
     next();
 });
 // Mounts all API routes under /api/v1
-app.use('/api/v1', api);
+app.use('/api/v1', api_v1);
+
+// Mounts all API routes under /api/v2
+app.use('/api/v2', api_v2);
 
 // Catch 404 and forward to error handler
 app.use((req, res, next) => {
diff --git a/src/libs/routes/api.js b/src/libs/routes_v1/api.js
similarity index 63%
rename from src/libs/routes/api.js
rename to src/libs/routes_v1/api.js
index f6715eaa..4b6a5630 100644
--- a/src/libs/routes/api.js
+++ b/src/libs/routes_v1/api.js
@@ -66,79 +66,79 @@ const idhml = require('./idhml');
 
 const oauth2 = require(`${libs}/middlewares/oauth2`);
 
-const verifyToken = require(`${libs}/routes/verifyToken`);
+const verifyToken = require(`${libs}/routes_v1/verifyToken`);
 
-const resetToken = require(`${libs}/routes/resetToken`);
+const resetToken = require(`${libs}/routes_v1/resetToken`);
 
-const educationYears = require(`${libs}/routes/educationYears`);
+const educationYears = require(`${libs}/routes_v1/educationYears`);
 
-const downloads = require(`${libs}/routes/downloads`);
+const downloads = require(`${libs}/routes_v1/downloads`);
 
-const infrastructure = require(`${libs}/routes/infrastructure`);
+const infrastructure = require(`${libs}/routes_v1/infrastructure`);
 
-const schoolInfrastructure = require(`${libs}/routes/schoolInfrastructure`);
+const schoolInfrastructure = require(`${libs}/routes_v1/schoolInfrastructure`);
 
-const distributionFactor = require(`${libs}/routes/distributionFactor`);
+const distributionFactor = require(`${libs}/routes_v1/distributionFactor`);
 
-const siope = require(`${libs}/routes/siope`);
+const siope = require(`${libs}/routes_v1/siope`);
 
-const verifyTeacher = require(`${libs}/routes/portalMec`);
+const verifyTeacher = require(`${libs}/routes_v1/portalMec`);
 
-const outOfSchool = require(`${libs}/routes/outOfSchool`);
+const outOfSchool = require(`${libs}/routes_v1/outOfSchool`);
 
-const classroomCount = require(`${libs}/routes/classroomCount`);
+const classroomCount = require(`${libs}/routes_v1/classroomCount`);
 
 const transport = require(`./transport`);
 
-const auxiliar = require(`${libs}/routes/auxiliar`);
+const auxiliar = require(`${libs}/routes_v1/auxiliar`);
 
-const dailyChargeAmount = require(`${libs}/routes/dailyChargeAmount`);
+const dailyChargeAmount = require(`${libs}/routes_v1/dailyChargeAmount`);
 
-const cub = require(`${libs}/routes/cub`);
+const cub = require(`${libs}/routes_v1/cub`);
 
-const classCount = require(`${libs}/routes/classCount`);
+const classCount = require(`${libs}/routes_v1/classCount`);
 
-const portalMecInep = require(`${libs}/routes/portalMecInep`);
+const portalMecInep = require(`${libs}/routes_v1/portalMecInep`);
 
-const enrollmentProjection = require(`${libs}/routes/enrollmentProjection`);
+const enrollmentProjection = require(`${libs}/routes_v1/enrollmentProjection`);
 
-const employees = require(`${libs}/routes/employees`);
+const employees = require(`${libs}/routes_v1/employees`);
 
-const financial = require(`${libs}/routes/financial`);
+const financial = require(`${libs}/routes_v1/financial`);
 
-const universityEnrollment = require(`${libs}/routes/universityEnrollment`);
+const universityEnrollment = require(`${libs}/routes_v1/universityEnrollment`);
 
-const courseCount = require(`${libs}/routes/courseCount`);
+const courseCount = require(`${libs}/routes_v1/courseCount`);
 
-const university = require(`${libs}/routes/university`);
+const university = require(`${libs}/routes_v1/university`);
 
-const universityTeacher = require(`${libs}/routes/universityTeacher`);
+const universityTeacher = require(`${libs}/routes_v1/universityTeacher`);
 
-const educationalBudget = require(`${libs}/routes/educationalBudget`);
+const educationalBudget = require(`${libs}/routes_v1/educationalBudget`);
 
-const schoolLocation = require(`${libs}/routes/schoolLocation`);
+const schoolLocation = require(`${libs}/routes_v1/schoolLocation`);
 
-const studentsAee = require(`${libs}/routes/studentsAee`);
+const studentsAee = require(`${libs}/routes_v1/studentsAee`);
 
-const mesoregion = require(`${libs}/routes/mesoregion`);
+const mesoregion = require(`${libs}/routes_v1/mesoregion`);
 
-const microregion = require(`${libs}/routes/microregion`);
+const microregion = require(`${libs}/routes_v1/microregion`);
 
-const location = require(`${libs}/routes/location`);
+const location = require(`${libs}/routes_v1/location`);
 
-const disciplines = require(`${libs}/routes/disciplines`);
+const disciplines = require(`${libs}/routes_v1/disciplines`);
 
-const universityLocalOffer = require(`${libs}/routes/universityLocalOffer`);
+const universityLocalOffer = require(`${libs}/routes_v1/universityLocalOffer`);
 
-const message = require(`${libs}/routes/message`);
+const message = require(`${libs}/routes_v1/message`);
 
-const courseStudents = require(`${libs}/routes/courseStudents`);
+const courseStudents = require(`${libs}/routes_v1/courseStudents`);
 
 api.get('/', (req, res) => {
-    res.json({ msg: 'SimCAQ API is running' });
+    res.json({ msg: 'SimCAQ API v1 is running' });
 });
 
-// mount API routes
+// mount API routes_v1
 api.use('/user', user);
 api.use('/simulation', simulation);
 api.use('/class', classes);
diff --git a/src/libs/routes/auxiliar.js b/src/libs/routes_v1/auxiliar.js
similarity index 100%
rename from src/libs/routes/auxiliar.js
rename to src/libs/routes_v1/auxiliar.js
diff --git a/src/libs/routes/city.js b/src/libs/routes_v1/city.js
similarity index 100%
rename from src/libs/routes/city.js
rename to src/libs/routes_v1/city.js
diff --git a/src/libs/routes/class.js b/src/libs/routes_v1/class.js
similarity index 100%
rename from src/libs/routes/class.js
rename to src/libs/routes_v1/class.js
diff --git a/src/libs/routes/classCount.js b/src/libs/routes_v1/classCount.js
similarity index 100%
rename from src/libs/routes/classCount.js
rename to src/libs/routes_v1/classCount.js
diff --git a/src/libs/routes/classroom.js b/src/libs/routes_v1/classroom.js
similarity index 100%
rename from src/libs/routes/classroom.js
rename to src/libs/routes_v1/classroom.js
diff --git a/src/libs/routes/classroomCount.js b/src/libs/routes_v1/classroomCount.js
similarity index 100%
rename from src/libs/routes/classroomCount.js
rename to src/libs/routes_v1/classroomCount.js
diff --git a/src/libs/routes/courseCount.js b/src/libs/routes_v1/courseCount.js
similarity index 100%
rename from src/libs/routes/courseCount.js
rename to src/libs/routes_v1/courseCount.js
diff --git a/src/libs/routes/courseStudents.js b/src/libs/routes_v1/courseStudents.js
similarity index 100%
rename from src/libs/routes/courseStudents.js
rename to src/libs/routes_v1/courseStudents.js
diff --git a/src/libs/routes/cub.js b/src/libs/routes_v1/cub.js
similarity index 100%
rename from src/libs/routes/cub.js
rename to src/libs/routes_v1/cub.js
diff --git a/src/libs/routes/dailyChargeAmount.js b/src/libs/routes_v1/dailyChargeAmount.js
similarity index 100%
rename from src/libs/routes/dailyChargeAmount.js
rename to src/libs/routes_v1/dailyChargeAmount.js
diff --git a/src/libs/routes/disciplines.js b/src/libs/routes_v1/disciplines.js
similarity index 100%
rename from src/libs/routes/disciplines.js
rename to src/libs/routes_v1/disciplines.js
diff --git a/src/libs/routes/distributionFactor.js b/src/libs/routes_v1/distributionFactor.js
similarity index 100%
rename from src/libs/routes/distributionFactor.js
rename to src/libs/routes_v1/distributionFactor.js
diff --git a/src/libs/routes/downloads.js b/src/libs/routes_v1/downloads.js
similarity index 100%
rename from src/libs/routes/downloads.js
rename to src/libs/routes_v1/downloads.js
diff --git a/src/libs/routes/educationYears.js b/src/libs/routes_v1/educationYears.js
similarity index 100%
rename from src/libs/routes/educationYears.js
rename to src/libs/routes_v1/educationYears.js
diff --git a/src/libs/routes/educationalBudget.js b/src/libs/routes_v1/educationalBudget.js
similarity index 100%
rename from src/libs/routes/educationalBudget.js
rename to src/libs/routes_v1/educationalBudget.js
diff --git a/src/libs/routes/employees.js b/src/libs/routes_v1/employees.js
similarity index 100%
rename from src/libs/routes/employees.js
rename to src/libs/routes_v1/employees.js
diff --git a/src/libs/routes/enrollment.js b/src/libs/routes_v1/enrollment.js
similarity index 100%
rename from src/libs/routes/enrollment.js
rename to src/libs/routes_v1/enrollment.js
diff --git a/src/libs/routes/enrollmentProjection.js b/src/libs/routes_v1/enrollmentProjection.js
similarity index 100%
rename from src/libs/routes/enrollmentProjection.js
rename to src/libs/routes_v1/enrollmentProjection.js
diff --git a/src/libs/routes/financial.js b/src/libs/routes_v1/financial.js
similarity index 100%
rename from src/libs/routes/financial.js
rename to src/libs/routes_v1/financial.js
diff --git a/src/libs/routes/glossEnrollmentRatio.js b/src/libs/routes_v1/glossEnrollmentRatio.js
similarity index 100%
rename from src/libs/routes/glossEnrollmentRatio.js
rename to src/libs/routes_v1/glossEnrollmentRatio.js
diff --git a/src/libs/routes/idhm.js b/src/libs/routes_v1/idhm.js
similarity index 100%
rename from src/libs/routes/idhm.js
rename to src/libs/routes_v1/idhm.js
diff --git a/src/libs/routes/idhme.js b/src/libs/routes_v1/idhme.js
similarity index 100%
rename from src/libs/routes/idhme.js
rename to src/libs/routes_v1/idhme.js
diff --git a/src/libs/routes/idhml.js b/src/libs/routes_v1/idhml.js
similarity index 100%
rename from src/libs/routes/idhml.js
rename to src/libs/routes_v1/idhml.js
diff --git a/src/libs/routes/idhmr.js b/src/libs/routes_v1/idhmr.js
similarity index 100%
rename from src/libs/routes/idhmr.js
rename to src/libs/routes_v1/idhmr.js
diff --git a/src/libs/routes/infrastructure.js b/src/libs/routes_v1/infrastructure.js
similarity index 100%
rename from src/libs/routes/infrastructure.js
rename to src/libs/routes_v1/infrastructure.js
diff --git a/src/libs/routes/liquidEnrollmentRatio.js b/src/libs/routes_v1/liquidEnrollmentRatio.js
similarity index 100%
rename from src/libs/routes/liquidEnrollmentRatio.js
rename to src/libs/routes_v1/liquidEnrollmentRatio.js
diff --git a/src/libs/routes/location.js b/src/libs/routes_v1/location.js
similarity index 100%
rename from src/libs/routes/location.js
rename to src/libs/routes_v1/location.js
diff --git a/src/libs/routes/mesoregion.js b/src/libs/routes_v1/mesoregion.js
similarity index 100%
rename from src/libs/routes/mesoregion.js
rename to src/libs/routes_v1/mesoregion.js
diff --git a/src/libs/routes/message.js b/src/libs/routes_v1/message.js
similarity index 100%
rename from src/libs/routes/message.js
rename to src/libs/routes_v1/message.js
diff --git a/src/libs/routes/microregion.js b/src/libs/routes_v1/microregion.js
similarity index 100%
rename from src/libs/routes/microregion.js
rename to src/libs/routes_v1/microregion.js
diff --git a/src/libs/routes/outOfSchool.js b/src/libs/routes_v1/outOfSchool.js
similarity index 100%
rename from src/libs/routes/outOfSchool.js
rename to src/libs/routes_v1/outOfSchool.js
diff --git a/src/libs/routes/pibpercapita.js b/src/libs/routes_v1/pibpercapita.js
similarity index 100%
rename from src/libs/routes/pibpercapita.js
rename to src/libs/routes_v1/pibpercapita.js
diff --git a/src/libs/routes/population.js b/src/libs/routes_v1/population.js
similarity index 100%
rename from src/libs/routes/population.js
rename to src/libs/routes_v1/population.js
diff --git a/src/libs/routes/portalMec.js b/src/libs/routes_v1/portalMec.js
similarity index 100%
rename from src/libs/routes/portalMec.js
rename to src/libs/routes_v1/portalMec.js
diff --git a/src/libs/routes/portalMecInep.js b/src/libs/routes_v1/portalMecInep.js
similarity index 100%
rename from src/libs/routes/portalMecInep.js
rename to src/libs/routes_v1/portalMecInep.js
diff --git a/src/libs/routes/rateSchool.js b/src/libs/routes_v1/rateSchool.js
similarity index 100%
rename from src/libs/routes/rateSchool.js
rename to src/libs/routes_v1/rateSchool.js
diff --git a/src/libs/routes/region.js b/src/libs/routes_v1/region.js
similarity index 100%
rename from src/libs/routes/region.js
rename to src/libs/routes_v1/region.js
diff --git a/src/libs/routes/resetToken.js b/src/libs/routes_v1/resetToken.js
similarity index 100%
rename from src/libs/routes/resetToken.js
rename to src/libs/routes_v1/resetToken.js
diff --git a/src/libs/routes_v1/school.js b/src/libs/routes_v1/school.js
new file mode 100644
index 00000000..73b765e2
--- /dev/null
+++ b/src/libs/routes_v1/school.js
@@ -0,0 +1,663 @@
+const express = require('express');
+
+const schoolApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const passport = require('passport');
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+let rqfCount = new ReqQueryFields();
+
+// Return location
+schoolApp.get('/year_range', cache('15 day'), (req, res, next) => {
+    req.sql.from('escola')
+    .field('MIN(escola.ano_censo)', 'start_year')
+    .field('MAX(escola.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+schoolApp.get('/years', cache('15 day'), (req, res, next) => {
+    req.sql.from('escola').
+    field('DISTINCT escola.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+schoolApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'escola\'');
+    next();
+}, query, response('source'));
+
+schoolApp.get('/location', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+schoolApp.get('/diff_location', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: 0, name: "Não está em localidade diferenciada"},
+        {id: 1, name: "Área de assentamento"},
+        {id: 2, name: "Terra indígena"},
+        {id: 3, name: "Terra remanescente de quilombos"},
+    ];
+    next();
+}, response('diff_location'));
+
+schoolApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+schoolApp.get('/adm_dependency_detailed', cache('15 day'), (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 8; i++) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+schoolApp.get('/government_agreement', cache('15 day'), (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.governmentAgreement("null")
+    }];
+    for(let i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.governmentAgreement(i)
+        });
+    };
+    next();
+}, response('government_agreement'));
+
+schoolApp.get('/agreement', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Municipal'},
+        {id: 2, name: 'Estadual'},
+        {id: 3, name: 'Estadual e Municipal'}
+    ];
+    next();
+}, response('agreement'));
+
+schoolApp.get('/education_day_care_child', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Declarado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('education_day_care_child'));
+
+schoolApp.get('/education_preschool_child', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Declarado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('education_preschool_child'));
+
+schoolApp.get('/education_begin_elementary_school', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Declarado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('education_begin_elementary_school'));
+
+schoolApp.get('/education_end_elementary_school', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Declarado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('education_end_elementary_school'));
+
+schoolApp.get('/education_middle_school', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Declarado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('education_middle_school'));
+
+schoolApp.get('/education_professional', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Declarado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('education_professional'));
+
+schoolApp.get('/education_eja', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Declarado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('education_eja'));
+
+
+schoolApp.get('/arrangement', cache('15 day'), (req, res, next) => {
+    req.result = [
+        {id: 0, name: 'Creche'},
+        {id: 1, name: 'Pré Escola'},
+        {id: 2, name: 'Ensino Fundamental - AI'},
+        {id: 3, name: 'Ensino Fundamental - AF'},
+        {id: 4, name: 'Ed. Infantil Unificada/Multietapa/Multissérie/Correção fluxo'},
+        {id: 5, name: 'Ensino Médio'},
+        {id: 6, name: 'Ensino EJA'},
+        {id: 7, name: 'Educação Profissional'},
+        {id: 8, name: 'Educação Especial Exclusiva'}
+    ];
+    next();
+}, response('arrangement'));
+
+schoolApp.get('/integral_time', cache('15 day'), (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <= 2; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.integralTime(i)
+        });
+    }
+    next();
+}, response('integral_time'));
+
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'id',
+    table: 'escola',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo',
+        table: 'escola'
+    }
+}).addField({
+    name: 'search',
+    field: true,
+    where: true
+}).addValueToField({
+    name: 'city_name',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    dontGroup: true,
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'nome'
+    },
+    join: {
+      primary: 'id',
+      foreign: 'municipio_id',
+      foreignTable: 'escola'
+    }
+}, 'search')
+.addValueToField({
+    name: 'state_name',
+    table: 'estado',
+    tableField: 'nome',
+    resultField: 'state_name',
+    dontGroup: true,
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'sigla'
+    },
+    join: {
+      primary: 'id',
+      foreign: 'estado_id',
+      foreignTable: 'escola'
+    }
+}, 'search').addValue({
+    name: 'diff_location',
+    table: 'escola',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+});
+
+rqfCount.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'id',
+    table: 'escola',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}, 'filter').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo',
+        table: 'escola'
+    }
+}).addValue({
+    name: 'location',
+    table: 'escola',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'diff_location',
+    table: 'escola',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'escola',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'arrangement',
+    table: 'escola',
+    tableField: 'arranjo',
+    resultField: 'arrangement_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'arranjo'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'escola',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'escola',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'government_agreement',
+    table: 'escola',
+    tableField: 'dependencia_convenio_publico',
+    resultField: 'government_agreement_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_convenio_publico'
+    }
+}).addValue({
+    name: 'integral_time',
+    table: 'escola',
+    tableField: 'tempo_integral',
+    resultField: 'integral_time_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'tempo_integral'
+    }
+}).addValue({
+    name: 'agreement',
+    table: 'escola',
+    tableField: 'tipo_convenio_pp',
+    resultField: 'agreement_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'tipo_convenio_pp'
+    }
+}).addValue({
+    name: 'education_day_care_child',
+    table: 'escola',
+    tableField: 'reg_infantil_creche_t1',
+    resultField: 'education_day_care_child_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'reg_infantil_creche_t1'
+    }
+}).addValue({
+    name: 'education_preschool_child',
+    table: 'escola',
+    tableField: 'reg_infantil_preescola_t1',
+    resultField: 'education_preschool_child_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'reg_infantil_preescola_t1'
+    }
+}).addValue({
+    name: 'education_begin_elementary_school',
+    table: 'escola',
+    tableField: 'reg_fund_ai_t1',
+    resultField: 'education_begin_elementary_school_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'reg_fund_ai_t1'
+    }
+}).addValue({
+    name: 'education_end_elementary_school',
+    table: 'escola',
+    tableField: 'reg_fund_af_t1',
+    resultField: 'education_end_elementary_school_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'reg_fund_af_t1'
+    }
+}).addValue({
+    name: 'education_middle_school',
+    table: 'escola',
+    tableField: 'reg_medio_medio_t1',
+    resultField: 'education_middle_school_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'reg_medio_medio_t1'
+    }
+}).addValue({
+    name: 'education_professional',
+    table: 'escola',
+    tableField: 'educacao_profissional',
+    resultField: 'education_professional_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'educacao_profissional'
+    }
+}).addValue({
+    name: 'education_eja',
+    table: 'escola',
+    tableField: 'ensino_eja',
+    resultField: 'education_eja_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'ensino_eja'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'school_building',
+    table: 'escola',
+    tableField: 'local_func_predio_escolar',
+    resultField: 'school_building',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'local_func_predio_escolar'
+    }
+});
+schoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+
+    req.sql.from('escola')
+        .field('escola.id')
+        .field('escola.ano_censo', 'year')
+        .field('escola.nome_escola', 'name')
+        .field('escola.estado_id', 'state_id')
+        .field('escola.municipio_id', 'city_id');
+    next();
+}, query, response('school'));
+
+schoolApp.get('/count', cache('15 day'), rqfCount.parse(), (req, res, next) => {
+	let arrang = ["arranjo_creche", "arranjo_pre", "arranjo_fundamental_ai", "arranjo_fundamental_af", "arranjo_multietapa", "arranjo_ensino_medio", "ensino_eja", "educacao_profissional", "ensino_especial"];
+
+    req.sql.from('escola')
+        .field('COUNT(escola.id)', 'total')
+        .field("'Brasil'", 'name')
+        .field('escola.ano_censo', 'year')
+        .group('escola.ano_censo')
+        .order('escola.ano_censo')
+        .where('escola.situacao_funcionamento_pareada = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)')
+
+	//Transforma a query em OR se tiver o filtro do arranjo
+	if (req.filter.arrangement) {
+		let arrangementQuery = "";
+		for (let i = 0; i < req.filter.arrangement.length - 1; i++) {
+			arrangementQuery += 'escola.' + arrang[req.filter.arrangement[i]] + ' = 1 OR ';
+		}
+		// o ultimo elemento precisa ser sem o OR
+		arrangementQuery += 'escola.' + arrang[req.filter.arrangement[req.filter.arrangement.length - 1]] + ' = 1';
+		req.sql.where('' + arrangementQuery);
+	}
+	delete req.filter.arrangement
+    next();
+}, rqfCount.build(), query, id2str.transform(), addMissing(rqfCount), response('school'));
+
+module.exports = schoolApp;
diff --git a/src/libs/routes/schoolInfrastructure.js b/src/libs/routes_v1/schoolInfrastructure.js
similarity index 100%
rename from src/libs/routes/schoolInfrastructure.js
rename to src/libs/routes_v1/schoolInfrastructure.js
diff --git a/src/libs/routes/schoolLocation.js b/src/libs/routes_v1/schoolLocation.js
similarity index 100%
rename from src/libs/routes/schoolLocation.js
rename to src/libs/routes_v1/schoolLocation.js
diff --git a/src/libs/routes/simulation.js b/src/libs/routes_v1/simulation.js
similarity index 100%
rename from src/libs/routes/simulation.js
rename to src/libs/routes_v1/simulation.js
diff --git a/src/libs/routes/siope.js b/src/libs/routes_v1/siope.js
similarity index 100%
rename from src/libs/routes/siope.js
rename to src/libs/routes_v1/siope.js
diff --git a/src/libs/routes/spatial.js b/src/libs/routes_v1/spatial.js
similarity index 100%
rename from src/libs/routes/spatial.js
rename to src/libs/routes_v1/spatial.js
diff --git a/src/libs/routes/state.js b/src/libs/routes_v1/state.js
similarity index 100%
rename from src/libs/routes/state.js
rename to src/libs/routes_v1/state.js
diff --git a/src/libs/routes/studentsAee.js b/src/libs/routes_v1/studentsAee.js
similarity index 100%
rename from src/libs/routes/studentsAee.js
rename to src/libs/routes_v1/studentsAee.js
diff --git a/src/libs/routes/teacher.js b/src/libs/routes_v1/teacher.js
similarity index 100%
rename from src/libs/routes/teacher.js
rename to src/libs/routes_v1/teacher.js
diff --git a/src/libs/routes/transport.js b/src/libs/routes_v1/transport.js
similarity index 100%
rename from src/libs/routes/transport.js
rename to src/libs/routes_v1/transport.js
diff --git a/src/libs/routes/university.js b/src/libs/routes_v1/university.js
similarity index 100%
rename from src/libs/routes/university.js
rename to src/libs/routes_v1/university.js
diff --git a/src/libs/routes/universityEnrollment.js b/src/libs/routes_v1/universityEnrollment.js
similarity index 100%
rename from src/libs/routes/universityEnrollment.js
rename to src/libs/routes_v1/universityEnrollment.js
diff --git a/src/libs/routes/universityLocalOffer.js b/src/libs/routes_v1/universityLocalOffer.js
similarity index 100%
rename from src/libs/routes/universityLocalOffer.js
rename to src/libs/routes_v1/universityLocalOffer.js
diff --git a/src/libs/routes/universityTeacher.js b/src/libs/routes_v1/universityTeacher.js
similarity index 100%
rename from src/libs/routes/universityTeacher.js
rename to src/libs/routes_v1/universityTeacher.js
diff --git a/src/libs/routes/user.js b/src/libs/routes_v1/user.js
similarity index 100%
rename from src/libs/routes/user.js
rename to src/libs/routes_v1/user.js
diff --git a/src/libs/routes/verifyToken.js b/src/libs/routes_v1/verifyToken.js
similarity index 100%
rename from src/libs/routes/verifyToken.js
rename to src/libs/routes_v1/verifyToken.js
diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
new file mode 100644
index 00000000..906af9cf
--- /dev/null
+++ b/src/libs/routes_v2/api.js
@@ -0,0 +1,196 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const api = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const config = require(`${libs}/config`);
+
+const classes = require('./class');
+
+const enrollment = require('./enrollment');
+
+const state = require('./state');
+
+const region = require('./region');
+
+const city = require('./city');
+
+const school = require('./school');
+
+const simulation = require('./simulation');
+
+const user = require('./user');
+
+const classroom = require('./classroom');
+
+const teacher = require('./teacher');
+
+const idhme = require('./idhme');
+
+const pibpercapita = require('./pibpercapita')
+
+const population = require('./population')
+
+const rateSchool = require('./rateSchool')
+
+const glossEnrollmentRatio = require('./glossEnrollmentRatio')
+
+const liquidEnrollmentRatio = require('./liquidEnrollmentRatio')
+
+const idhm = require('./idhm');
+
+const idhmr = require('./idhmr');
+
+const idhml = require('./idhml');
+
+const oauth2 = require(`${libs}/middlewares/oauth2`);
+
+const verifyToken = require(`${libs}/routes_v2/verifyToken`);
+
+const resetToken = require(`${libs}/routes_v2/resetToken`);
+
+const educationYears = require(`${libs}/routes_v2/educationYears`);
+
+const downloads = require(`${libs}/routes_v2/downloads`);
+
+const infrastructure = require(`${libs}/routes_v2/infrastructure`);
+
+const schoolInfrastructure = require(`${libs}/routes_v2/schoolInfrastructure`);
+
+const distributionFactor = require(`${libs}/routes_v2/distributionFactor`);
+
+const siope = require(`${libs}/routes_v2/siope`);
+
+const verifyTeacher = require(`${libs}/routes_v2/portalMec`);
+
+const outOfSchool = require(`${libs}/routes_v2/outOfSchool`);
+
+const classroomCount = require(`${libs}/routes_v2/classroomCount`);
+
+const transport = require(`./transport`);
+
+const auxiliar = require(`${libs}/routes_v2/auxiliar`);
+
+const dailyChargeAmount = require(`${libs}/routes_v2/dailyChargeAmount`);
+
+const cub = require(`${libs}/routes_v2/cub`);
+
+const classCount = require(`${libs}/routes_v2/classCount`);
+
+const portalMecInep = require(`${libs}/routes_v2/portalMecInep`);
+
+const enrollmentProjection = require(`${libs}/routes_v2/enrollmentProjection`);
+
+const employees = require(`${libs}/routes_v2/employees`);
+
+const financial = require(`${libs}/routes_v2/financial`);
+
+const universityEnrollment = require(`${libs}/routes_v2/universityEnrollment`);
+
+const courseCount = require(`${libs}/routes_v2/courseCount`);
+
+const university = require(`${libs}/routes_v2/university`);
+
+const universityTeacher = require(`${libs}/routes_v2/universityTeacher`);
+
+const educationalBudget = require(`${libs}/routes_v2/educationalBudget`);
+
+const schoolLocation = require(`${libs}/routes_v2/schoolLocation`);
+
+const studentsAee = require(`${libs}/routes_v2/studentsAee`);
+
+const mesoregion = require(`${libs}/routes_v2/mesoregion`);
+
+const microregion = require(`${libs}/routes_v2/microregion`);
+
+const location = require(`${libs}/routes_v2/location`);
+
+const disciplines = require(`${libs}/routes_v2/disciplines`);
+
+const universityLocalOffer = require(`${libs}/routes_v2/universityLocalOffer`);
+
+const message = require(`${libs}/routes_v2/message`);
+
+const courseStudents = require(`${libs}/routes_v2/courseStudents`);
+
+api.get('/', (req, res) => {
+    res.json({ msg: 'SimCAQ API v2 is running' });
+});
+
+// mount API routes_v2
+api.use('/user', user);
+api.use('/simulation', simulation);
+api.use('/class', classes);
+api.use('/enrollment', enrollment);
+api.use('/state', state);
+api.use('/region', region);
+api.use('/city', city);
+api.use('/school', school);
+api.use('/classroom', classroom);
+api.use('/teacher', teacher);
+api.use('/idhmr', idhmr);
+api.use('/idhm', idhm);
+api.use('/idhme', idhme);
+api.use('/pibpercapita', pibpercapita);
+api.use('/population', population);
+api.use('/rate_school', rateSchool);
+api.use('/gloss_enrollment_ratio', glossEnrollmentRatio);
+api.use('/liquid_enrollment_ratio', liquidEnrollmentRatio);
+api.use('/idhml', idhml);
+api.use('/auth/token', oauth2.token);
+api.use('/verify', verifyToken);
+api.use('/reset', resetToken);
+api.use('/education_years', educationYears);
+api.use('/downloads', downloads);
+api.use('/infrastructure', infrastructure);
+api.use('/school_infrastructure', schoolInfrastructure);
+api.use('/distribution_factor', distributionFactor);
+api.use('/siope', siope);
+api.use('/out_of_school', outOfSchool);
+api.use('/classroom_count', classroomCount);
+api.use('/daily_charge_amount', dailyChargeAmount);
+api.use('/transport', transport);
+api.use('/cub', cub);
+api.use('/auxiliar', auxiliar);
+api.use('/verify_teacher', verifyTeacher);
+api.use('/class_count', classCount);
+api.use('/portal_mec_inep', portalMecInep);
+api.use('/enrollment_projection', enrollmentProjection);
+api.use('/employees', employees);
+api.use('/financial', financial);
+api.use('/university_enrollment', universityEnrollment);
+api.use('/university', university);
+api.use('/university_teacher', universityTeacher);
+api.use('/course_count', courseCount);
+api.use('/school_location', schoolLocation);
+api.use('/students_aee', studentsAee);
+api.use('/mesoregion', mesoregion);
+api.use('/microregion', microregion);
+api.use('/location', location);
+api.use('/disciplines', disciplines);
+api.use('/universityLocalOffer', universityLocalOffer);
+api.use('/message', message);
+api.use('/course_students', courseStudents);
+
+module.exports = api;
diff --git a/src/libs/routes_v2/auxiliar.js b/src/libs/routes_v2/auxiliar.js
new file mode 100644
index 00000000..ef538961
--- /dev/null
+++ b/src/libs/routes_v2/auxiliar.js
@@ -0,0 +1,397 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const auxiliarApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]}  }).middleware;
+
+let rqf = new ReqQueryFields();
+
+auxiliarApp.use(cache('15 day'));
+
+auxiliarApp.get('/year_range', (req, res, next) => {
+    req.sql.from('docente')
+    .field('MIN(docente.ano_censo)', 'start_year')
+    .field('MAX(docente.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+auxiliarApp.get('/years', (req, res, next) => {
+    req.sql.from('docente').
+    field('DISTINCT docente.ano_censo', 'year')
+    next();
+}, query, response('years'));
+
+auxiliarApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'docente\'');
+    next();
+}, query, response('source'))
+
+auxiliarApp.get('/education_type', (req, res, next) => {
+    req.sql.from('docente')
+    .field('DISTINCT nivel_tipo_formacao', 'id')
+    .order('id');
+    next();
+}, query, (req, res, next) => {
+    req.result.forEach((result) => {
+        result.name = id2str.educationType(result.id);
+    });
+    next();
+}, response('education_type'));
+
+auxiliarApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 7; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+auxiliarApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+auxiliarApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+auxiliarApp.get('/diff_location', (req, res, next) => {
+    req.result = [
+        {id: 0, name: "A escola não está em localidade diferenciada"},
+        {id: 1, name: "Área de assentamento"},
+        {id: 2, name: "Terra indígena"},
+        {id: 3, name: "Terra remanescente de quilombos"},
+    ];
+    next();
+}, response('diff_location'));
+
+auxiliarApp.get('/education_level_mod', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 12; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.educationLevelMod(i)
+        });
+    }
+
+    req.result.push({
+        id: 99,
+        name: id2str.educationLevelMod(99)
+    });
+    next();
+}, response('education_level_mod'));
+
+auxiliarApp.get('/gender', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Masculino'},
+        {id: 2, name: 'Feminino'}
+    ];
+    next();
+}, response('gender'));
+
+auxiliarApp.get('/ethnic_group', (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <=5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ethnicGroup(i)
+        });
+    }
+    next();
+}, response('ethnic_group'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'adm_dependency',
+    table: 'docente',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'docente',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'education_type',
+    table: 'docente',
+    tableField: 'nivel_tipo_formacao',
+    resultField: 'education_type_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'nivel_tipo_formacao'
+    }
+}).addValue({
+    name: 'education_level_mod',
+    table: 'docente',
+    tableField: 'etapas_mod_ensino_segmento_id',
+    resultField: 'education_level_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapas_mod_ensino_segmento_id'
+    }
+}).addValue({
+  name: 'region',
+  table: 'regiao',
+  tableField: ['nome', 'id'],
+  resultField: ['region_name', 'region_id'],
+  where: {
+      relation: '=',
+      type: 'integer',
+      field: 'id'
+  },
+  join: {
+      primary: 'id',
+      foreign: 'escola_regiao_id',
+      foreignTable: 'docente'
+  }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_estado_id',
+        foreignTable: 'docente'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'docente',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}, 'filter').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'docente'
+    }
+}, 'dims').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'docente'
+    }
+}, 'filter').addValue({
+    name: 'location',
+    table: 'docente',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'diff_location',
+    table: 'docente',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'docente',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'docente',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'gender',
+    table: 'docente',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'ethnic_group',
+    table: 'docente',
+    tableField: 'cor_raca',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca'
+    }
+});
+
+// LDE
+auxiliarApp.get('/', rqf.parse(), (req, res, next) => {
+  req.sql.field('COUNT(DISTINCT docente.id_docente)', 'total')
+  .field("'Brasil'", 'name')
+  .field('docente.ano_censo', 'year')
+  .from('docente')
+  .join('turma', null, 'docente.turma_id=turma.id AND docente.ano_censo=turma.ano_censo')
+  .group('docente.ano_censo')
+  .order('docente.ano_censo')
+  .where('(docente.tipo_docente = 2) AND \
+    ((docente.tipo_turma_id >= 0 AND docente.tipo_turma_id <= 3 AND docente.tipo_turma_atendimento_id is NULL) \
+    OR ((docente.tipo_turma_atendimento_id = 1 OR docente.tipo_turma_atendimento_id = 2) AND docente.tipo_turma_id is NULL)) \
+    AND (docente.ano_censo <> 2009 or (docente.escola_estado_id <> 42 AND docente.escola_estado_id <> 43))');     // não devemos trazer SC em 2009.
+  next();
+}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('auxiliar'));
+
+// SimCAQ
+auxiliarApp.get('/count', rqf.parse(), (req, res, next) => {
+    req.sql.field('COUNT(DISTINCT docente.id_docente)', 'total')
+   .field("'Brasil'", 'name')
+   .field('docente.ano_censo', 'year')
+   .from('docente')
+   .group('docente.ano_censo')
+   .order('docente.ano_censo')
+   .where('(tipo_turma_id <= 3) AND (tipo_docente=2) AND (dependencia_adm_id = 1 OR dependencia_adm_id = 2 OR dependencia_adm_id = 3) AND  \
+         (etapas_mod_ensino_segmento_id=1 OR etapas_mod_ensino_segmento_id=2 OR etapas_mod_ensino_segmento_id=4 OR \
+         etapas_mod_ensino_segmento_id=5 OR etapas_mod_ensino_segmento_id=6 OR etapas_mod_ensino_segmento_id=8 OR etapas_mod_ensino_segmento_id=9)');
+
+  next();
+}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('auxiliar'));
+
+module.exports = auxiliarApp;
diff --git a/src/libs/routes_v2/city.js b/src/libs/routes_v2/city.js
new file mode 100644
index 00000000..fc697a9f
--- /dev/null
+++ b/src/libs/routes_v2/city.js
@@ -0,0 +1,151 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const cityApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+cityApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'id',
+    table: 'municipio',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'municipio'
+    }
+}).addValue({
+    name: 'state_not',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '<>',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'municipio'
+    }
+}).addField({
+    name: 'search',
+    field: false,
+    where: true
+}).addValueToField({
+    name: 'name',
+    table: 'municipio',
+    tableField: 'nome',
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'nome'
+    }
+}, 'search').addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: 'mesorregiao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: 'microrregiao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id'
+    }
+}).addValueToField({
+    name: 'region',
+    table: 'estado',
+    tableField: 'regiao_id',
+    resultField: 'region_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'regiao_id',
+        table: 'estado'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'municipio'
+    }
+}, 'filter');
+
+// Return all cities
+cityApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('municipio')
+    .field('municipio.nome', 'name')
+    .field('municipio.id')
+    .field('municipio.estado_id', 'state_id')
+    .field('municipio.longitude', 'longitude')
+    .field('municipio.latitude', 'latitude')
+    .field('municipio.mesorregiao_id', 'mesoregion_id')
+    .field('municipio.microrregiao_id', 'microregion_id');
+    next();
+}, query, response('city'));
+
+
+module.exports = cityApp;
diff --git a/src/libs/routes_v2/class.js b/src/libs/routes_v2/class.js
new file mode 100644
index 00000000..4eca8cc3
--- /dev/null
+++ b/src/libs/routes_v2/class.js
@@ -0,0 +1,402 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const classApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqfCount = new ReqQueryFields();
+
+classApp.use(cache('15 day'));
+
+// Complete range of the enrollments dataset.
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+classApp.get('/year_range', (req, res, next) => {
+    req.sql.from('turma')
+    .field('MIN(turma.ano_censo)', 'start_year')
+    .field('MAX(turma.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+classApp.get('/years', (req, res, next) => {
+    req.sql.from('turma')
+    .field('DISTINCT turma.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+classApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'turma\'');
+    next();
+}, query, response('source'));
+
+classApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+classApp.get('/diff_location', (req, res, next) => {
+    req.result = [
+        {id: 0, name: "A escola não está em localidade diferenciada"},
+        {id: 1, name: "Área de assentamento"},
+        {id: 2, name: "Terra indígena"},
+        {id: 3, name: "Terra remanescente de quilombos"},
+    ];
+    next();
+}, response('diff_location'));
+
+// Returns all adm dependencies
+classApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+classApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 8; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+// Returns all periods avaible
+classApp.get('/period', (req, res, next) => {
+    req.result = [];
+    for(let i=1; i <= 3; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.period(i)
+        });
+    }
+    req.result.push({
+        id: 99,
+        name: id2str.period(99)
+    });
+    next();
+}, response('period'));
+
+// Returns integral-time avaible
+classApp.get('/integral_time', (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <= 2; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.integralTime(i)
+        });
+    }
+    next();
+}, response('integral_time'));
+
+// Returns all educational levels avaible
+classApp.get('/education_level_mod', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <=12; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.educationLevelMod(i)
+        });
+    }
+    next();
+}, response('education_level_mod'));
+
+classApp.get('/education_level_short', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não classificada'},
+        {id: 1, name: 'Creche'},
+        {id: 2, name: 'Pré-Escola'},
+        {id: 3, name: 'Ensino Fundamental - anos iniciais'},
+        {id: 4, name: 'Ensino Fundamental - anos finais'},
+        {id: 5, name: 'Ensino Médio'},
+        {id: 6, name: 'EJA'},
+        {id: 7, name: 'EE exclusiva'}
+    ];
+    next();
+}, response('education_level_short'));
+
+rqfCount.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'turma'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'turma'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'turma'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'turma'
+    }
+}, 'filter').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'turma'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'turma'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'turma'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'turma',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'turma',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name:'adm_dependency',
+    table: 'turma',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'location',
+    table: 'turma',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'diff_location',
+    table: 'turma',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'turma',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+  name:'education_level_mod',
+  table: 'turma',
+  tableField: 'etapas_mod_ensino_segmento_id',
+  resultField: 'education_level_mod_id',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'etapas_mod_ensino_segmento_id'
+  }
+}).addValue({
+    name: 'education_level_short',
+    table: 'turma',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'turma',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+  name:'period',
+  table: 'turma',
+  tableField: 'turma_turno_id',
+  resultField: 'period_id',
+  where: {
+      relation: '=',
+      type: 'integer',
+      field: 'turma_turno_id'
+  }
+}).addValue({
+  name:'integral_time',
+  table: 'turma',
+  tableField: 'tempo_integral',
+  resultField: 'integral_time_id',
+  where: {
+      relation: '=',
+      type: 'integer',
+      field: 'tempo_integral'
+  }
+}).addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'turma'
+    }
+}, 'dims').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'turma'
+    }
+}, 'filter').addValueToField({
+    name: 'period_not',
+    table: 'turma',
+    tableField: 'turma_turno_id',
+    resultField: 'period_id',
+    where: {
+        relation: '<>',
+        type: 'integer',
+        field: 'turma_turno_id'
+    }
+}, 'filter');
+
+
+classApp.get('/', rqfCount.parse(), (req, res, next) => {
+    req.sql.field('COUNT(turma.id)', 'total')
+    .field("'Brasil'", 'name')
+    .field('turma.ano_censo', 'year')
+    .from('turma')
+    .group('turma.ano_censo')
+    .order('turma.ano_censo')
+    .where('(turma.tipo_turma_id >= 0 AND turma.tipo_turma_id <= 3 AND turma.tipo_atendimento_id is NULL) \
+        OR ((turma.tipo_atendimento_id = 1 OR turma.tipo_atendimento_id = 2) AND turma.tipo_turma_id is NULL) ');
+    next();
+}, rqfCount.build(), query, addMissing(rqfCount), id2str.transform(), response('class'));
+
+module.exports = classApp;
diff --git a/src/libs/routes_v2/classCount.js b/src/libs/routes_v2/classCount.js
new file mode 100644
index 00000000..ffdbe34f
--- /dev/null
+++ b/src/libs/routes_v2/classCount.js
@@ -0,0 +1,455 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const classCountApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+rqf.addField({
+  name: 'filter',
+  field: false,
+  where: true
+}).addField({
+  name: 'dims',
+  field: true,
+  where: false
+}).addValueToField({
+  name: 'city',
+  table: 'municipio',
+  tableField: ['nome', 'id'],
+  resultField: ['city_name', 'city_id'],
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'municipio_id',
+    table: 'turma'
+  },
+  join: {
+    primary: 'id',
+    foreign: 'municipio_id',
+    foreignTable: 'turma'
+  }
+}, 'dims').addValueToField({
+  name: 'city',
+  table: 'municipio',
+  tableField: 'nome',
+  resultField: 'city_name',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'municipio_id',
+    table: 'turma'
+  },
+  join: {
+    primary: 'id',
+    foreign: 'municipio_id',
+    foreignTable: 'turma'
+  }
+}, 'filter')
+.addValue({
+  name: 'state',
+  table: 'estado',
+  tableField: ['nome', 'id'],
+  resultField: ['state_name', 'state_id'],
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'estado_id',
+    table: 'turma'
+  },
+  join: {
+    primary: 'id',
+    foreign: 'estado_id',
+    foreignTable: 'turma'
+  }
+}).addValue({
+  name: 'region',
+  table: 'regiao',
+  tableField: ['nome', 'id'],
+  resultField: ['region_name', 'region_id'],
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'id'
+  },
+  join: {
+    primary: 'id',
+    foreign: 'regiao_id',
+    foreignTable: 'turma'
+  }
+}).addValue({
+  name: 'min_year',
+  table: 'turma',
+  tableField: 'ano_censo',
+  resultField: 'year',
+  where: {
+    relation: '>=',
+    type: 'integer',
+    field: 'ano_censo'
+  }
+}).addValue({
+  name: 'max_year',
+  table: 'turma',
+  tableField: 'ano_censo',
+  resultField: 'year',
+  where: {
+    relation: '<=',
+    type: 'integer',
+    field: 'ano_censo'
+  }
+}).addValue({
+  name:'adm_dependency',
+  table: 'turma',
+  tableField: 'dependencia_adm_id',
+  resultField: 'adm_dependency_id',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'dependencia_adm_id'
+  }
+}).addValue({
+  name: 'location',
+  table: 'turma',
+  tableField: 'localizacao_id',
+  resultField: 'location_id',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'localizacao_id'
+  }
+}).addValue({
+  name: 'rural_location',
+  table: 'turma',
+  tableField: 'localidade_area_rural',
+  resultField: 'rural_location_id',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'localidade_area_rural'
+  }
+}).addValue({
+  name:'education_level_mod',
+  table: 'turma',
+  tableField: 'etapas_mod_ensino_segmento_id',
+  resultField: 'education_level_mod_id',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'etapas_mod_ensino_segmento_id'
+  }
+}).addValue({
+  name:'education_level_short',
+  table: 'turma',
+  tableField: 'etapa_resumida',
+  resultField: 'education_level_short_id',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'etapa_resumida'
+  }
+}).addValue({
+  name: 'adm_dependency_detailed',
+  table: 'turma',
+  tableField: 'dependencia_adm_priv',
+  resultField: 'adm_dependency_detailed_id',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'dependencia_adm_priv'
+  }
+}).addValueToField({
+  name: 'school',
+  table: 'escola',
+  tableField: ['nome_escola', 'id'],
+  resultField: ['school_name', 'school_id'],
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'id'
+  },
+  join: {
+    primary: ['id', 'ano_censo'],
+    foreign: ['escola_id', 'ano_censo'],
+    foreignTable: 'turma'
+  }
+}, 'dims').addValueToField({
+  name: 'school',
+  table: 'escola',
+  tableField: 'nome_escola',
+  resultField: 'school_name',
+  where: {
+    relation: '=',
+    type: 'integer',
+    field: 'id'
+  },
+  join: {
+    primary: ['id', 'ano_censo'],
+    foreign: ['escola_id', 'ano_censo'],
+    foreignTable: 'turma'
+  }
+}, 'filter');
+
+classCountApp.get('/year_range', (req, res, next) => {
+  req.sql.from('escola')
+  .field('MIN(escola.ano_censo)', 'start_year')
+  .field('MAX(escola.ano_censo)', 'end_year');
+  next();
+}, query, response('range'));
+
+classCountApp.get('/years', (req, res, next) => {
+  req.sql.from('escola')
+  .field('DISTINCT escola.ano_censo', 'year');
+  next();
+}, query, response('years'));
+
+classCountApp.get('/adm_dependency', (req, res, next) => {
+  req.result = [];
+  for(let i = 1; i <= 4; ++i) {
+    req.result.push({
+      id: i,
+      name: id2str.admDependency(i)
+    });
+  };
+  next();
+}, response('adm_dependency'));
+
+classCountApp.get('/adm_dependency_detailed', (req, res, next) => {
+  req.result = [];
+  for(let i = 1; i <= 6; ++i) {
+    req.result.push({
+      id: i,
+      name: id2str.admDependencyPriv(i)
+    });
+  };
+  next();
+}, response('adm_dependency_detailed'));
+
+classCountApp.get('/location', (req, res, next) => {
+  req.result = [
+    {id: 1, name: 'Urbana'},
+    {id: 2, name: 'Rural'}
+  ];
+  next();
+}, response('location'));
+
+classCountApp.get('/rural_location', (req, res, next) => {
+  req.result = [
+    {id: 1, name: "Urbana"},
+    {id: 2, name: "Rural"},
+    {id: 3, name: "Rural - Área de assentamento"},
+    {id: 4, name: "Rural - Terra indígena"},
+    {id: 5, name: "Rural - Área remanescente de quilombos"},
+    {id: 6, name: "Rural - Unidade de uso sustentável"}
+  ];
+  next();
+}, response('rural_location'));
+
+classCountApp.get('/education_level_mod', (req, res, next) => {
+  req.result = [];
+  for(let i = 1; i <= 10; ++i) {
+    req.result.push({
+      id: i,
+      name: id2str.educationLevelMod(i)
+    });
+  }
+  req.result.push({
+    id: 99,
+    name: id2str.educationLevelMod(99)
+  });
+  next();
+}, response('education_level_mod'));
+
+classCountApp.get('/education_level_short', (req, res, next) => {
+  req.result = [];
+  for(let i = 1; i <= 7; ++i) {
+    req.result.push({
+      id: i,
+      name: id2str.educationLevelShort(i)
+    });
+  }
+  req.result.push({
+    id: 99,
+    name: id2str.educationLevelShort(99)
+  });
+  next();
+}, response('education_level_short'));
+
+classCountApp.get('/source', (req, res, next) => {
+  req.sql.from('fonte')
+  .field('fonte', 'source')
+  .where('tabela = \'turma\'');
+  next();
+}, query, response('source'));
+
+// Se a dimensão obrigatória do LDE (etapa de ensino) possui alguma etapa sem nenhum valor, então é criado um objeto
+// com média 0.0 e é inserido no resultado. Usada para não quebrar a sequência de etapas na tabela do LDE.
+function addNullFields(result) {
+  const firstYear = result[0].year;
+  var obj = result.filter(res => res.year == firstYear);
+  var prevFirstDimId = obj[0];
+  obj.forEach((d) => {
+    if((d["education_level_mod_id"] > prevFirstDimId["education_level_mod_id"]) && (prevFirstDimId["education_level_mod_id"] != 10) &&
+       (d["education_level_mod_id"] != prevFirstDimId["education_level_mod_id"] + 1)) {
+      let newObj = {};
+      Object.keys(prevFirstDimId).forEach((key) => {
+        newObj[key] = prevFirstDimId[key];
+      });
+      newObj.education_level_mod_id = d["education_level_mod_id"] - 1;
+      newObj.education_level_mod_name = id2str.educationLevelMod(d["education_level_mod_id"] - 1);
+      newObj.average = 0.0;
+      result.splice(result.indexOf(prevFirstDimId) + 1, 0, newObj);
+    }
+    prevFirstDimId = d;
+  });
+}
+
+function addZeroFields(result) {
+    let i;
+    for (i=0; i < result.length; i++) {
+        let hasTotal = result[i].hasOwnProperty("total");
+        if (hasTotal == true) {
+            result[i].average = 0.0;
+            result[i].median = 0.0;
+            result[i].stddev = 0.0;
+            result[i].first_qt = 0.0;
+            result[i].third_qt = 0.0;
+        }
+    }
+    return result;
+}
+
+// SimCAQ
+classCountApp.get('/count', rqf.parse(), (req, res, next) => {
+  req.sql.field("'Brasil'", 'name')
+   .field('turma.ano_censo', 'year')
+   .field('AVG(turma.num_matricula)', 'average')
+   .field('MEDIAN(turma.num_matricula)', 'median')
+   .field('STDDEV_POP(turma.num_matricula)', 'stddev')
+   .field('QUANTILE(turma.num_matricula, 0.25)', 'first_qt')
+   .field('QUANTILE(turma.num_matricula, 0.75)', 'third_qt')
+   .from('turma')
+   .group('turma.ano_censo')
+   .order('turma.ano_censo')
+   .where('turma.local_turma = 0 AND turma.dependencia_adm_id <= 3 AND ((turma.etapa_resumida >= 1 AND turma.etapa_resumida <= 7) OR turma.etapa_resumida = 99) AND turma.turma_turno_id <> 99')
+   next();
+}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('class_count'));
+
+// LDE
+// Essa rota não esta sendo usada.
+// Ela está com defeito e quando é chamada cai o banco.
+/*
+classCountApp.get('/', rqf.parse(), (req, res, next) => {
+  // Faz a consulta do número de alunos pelas dimensões
+  if(("education_level_mod" in req.dims) || ("education_level_mod" in req.filter) || ("education_level_short" in req.dims)) {
+    req.sql.field("'Brasil'", 'name')
+     .field('turma.ano_censo', 'year')
+     .field('AVG(turma.num_matricula)', 'average')
+     .field('MEDIAN(turma.num_matricula)', 'median')
+     .field('STDDEV_POP(turma.num_matricula)', 'stddev')
+     .field('QUANTILE(turma.num_matricula, 0.25)', 'first_qt')
+     .field('QUANTILE(turma.num_matricula, 0.75)', 'third_qt')
+     .from('turma')
+     .group('turma.ano_censo')
+     .order('turma.ano_censo')
+     .where('turma.tipo_turma_id = 0 AND turma.etapas_mod_ensino_segmento_id >= 1 AND turma.etapas_mod_ensino_segmento_id <= 10');
+     next();
+  } else {
+    res.status(400);
+    next({
+      status: 400,
+      message: 'Wrong/No filter specified'
+    });
+  }
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    req.partial = [];
+
+    // Caso tenha apenas uma dimensão, o indicador possuirá uma linha de total
+    if((req.dims) && (req.dims.size == 1)) {
+      req.partial = req.result;
+
+      // A linha de total deverá conter o valor total do ano que está no banco de dados, então usa o mesmo filtro de anos da consulta anterior
+      let yearFilter = {};
+      if("min_year" in req.filter)
+        yearFilter.min_year = req.filter.min_year;
+      if("max_year" in req.filter)
+        yearFilter.max_year = req.filter.max_year;
+
+      // Faz a consulta sem dimensões, do total do(s) ano(s) escolhido(s)
+      req.resetSql();
+      req.dims = {};
+      req.filter = yearFilter;
+      req.sql.field("'Brasil'", 'name')
+       .field('turma.ano_censo', 'year')
+       .field('AVG(turma.num_matricula)', 'average')
+       .field('MEDIAN(turma.num_matricula)', 'median')
+       .field('STDDEV_POP(turma.num_matricula)', 'stddev')
+       .field('QUANTILE(turma.num_matricula, 0.25)', 'first_qt')
+       .field('QUANTILE(turma.num_matricula, 0.75)', 'third_qt')
+       .from('turma')
+       .group('turma.ano_censo')
+       .order('turma.ano_censo')
+       .where('turma.tipo_turma_id = 0 AND turma.etapas_mod_ensino_segmento_id >= 1 AND turma.etapas_mod_ensino_segmento_id <= 10');
+     }
+    next();
+}, rqf.build(), query, addMissing(rqf), id2str.transform(), (req, res, next) => {
+  // Se tem apenas uma dimensão
+  if(req.partial.length > 0) {
+    const yearClassCount = req.result;
+    req.result = req.partial;
+
+    // Como a linha de total deve aparecer em um caso específico, ela é adicionada junto com a dimensão obrigatória
+    yearClassCount.forEach((result) => {
+      let obj = {};
+      obj = result;
+      obj.education_level_mod_name = "Total";
+      req.result.push(obj);
+    })
+  }
+  // Caso tenha mais de uma dimensão, retorna a consulta só pelas dimensões, sem linha de total
+  else {
+    addNullFields(req.result);
+    req.result = addZeroFields(req.result);
+  }
+
+  next();
+}, response('class_count'));
+*/
+
+module.exports = classCountApp;
diff --git a/src/libs/routes_v2/classroom.js b/src/libs/routes_v2/classroom.js
new file mode 100644
index 00000000..8e2b6a7e
--- /dev/null
+++ b/src/libs/routes_v2/classroom.js
@@ -0,0 +1,277 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const classroomApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+let rqfCount = new ReqQueryFields();
+
+//classroomApp.use(cache('15 day'));
+
+
+// Complete range of the enrollments dataset.
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+classroomApp.get('/year_range', (req, res, next) => {
+    req.sql.from('escola')
+    .field('MIN(escola.ano_censo)', 'start_year')
+    .field('MAX(escola.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+classroomApp.get('/years', (req, res, next) => {
+    req.sql.from('escola')
+    .field('DISTINCT escola.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+classroomApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'escola\'');
+    next();
+}, query, response('source'));
+
+classroomApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+classroomApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+classroomApp.get('/location', (req, res, next) => {
+    req.result = [
+		{id: 1, name: 'Urbana'},
+		{id: 2, name: 'Rural'}
+	];
+	next();
+}, response('location'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}, 'dims').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}, 'filter').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}, 'filter').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'escola',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'escola',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'location',
+    table: 'escola',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+});
+
+classroomApp.get('/', cache('15 day'), rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('escola')
+        .field('SUM(escola.qtde_salas_utilizadas_dentro)', 'total')
+        .field("'Brasil'", 'name')
+        .field('escola.ano_censo', 'year')
+        .group('escola.ano_censo')
+        .order('escola.ano_censo')
+        .where('escola.situacao_de_funcionamento = 1 AND escola.local_func_predio_escolar = 1')
+        .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1');
+    next();
+}, query, addMissing(rqf), id2str.transform(), (req, res, next) => {
+    if (req.dims.location && req.result.length < 2) {    // Garantimos que conterá as duas localizações no resultado para o simCAQ
+        let result = [];
+        for (let i = 1; i <= 2; i++) {
+            if (i !== req.result[0].location_id) {
+                let newObj = Object.assign({}, req.result[0]);
+                newObj.location_id = i;
+                newObj.total = 0;
+                newObj.location_name = (i == 1) ? "Urbana" : "Rural";
+                result.push(newObj);
+            }
+            else {
+                result.push(req.result[0]);
+            }
+        }
+
+        req.result = result;
+    }
+
+    next();
+}, response('classroom'));
+
+module.exports = classroomApp;
diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
new file mode 100644
index 00000000..4f76bed3
--- /dev/null
+++ b/src/libs/routes_v2/classroomCount.js
@@ -0,0 +1,1172 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+
+const express = require('express');
+
+const classroomCountApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}, 'filter').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}, 'dims').addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: 'nome',
+    resultField: 'state_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}, 'filter').addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id',
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}, 'dims').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id',
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: '@'
+    }
+}, 'dims').addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'school_year',
+    table: '@',
+    tableField: 'serie_ano_id',
+    resultField: 'school_year_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'serie_ano_id'
+    }
+}).addValue({
+    name: 'location',
+    table: '@',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'period',
+    table: '@',
+    tableField: 'turma_turno_id',
+    resultField: 'period_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'turma_turno_id'
+    }
+}).addValue({
+    name: 'school_building',
+    table: 'escola',
+    tableField: 'local_func_predio_escolar',
+    resultField: 'school_building',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'local_func_predio_escolar'
+    }
+}).addValue({
+    name: 'night_time',
+    table: 'matricula',
+    tableField: 'periodo_noturno',
+    resultField: 'night_time',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'periodo_noturno'
+    }
+}).addValue({
+    name: 'formation_level',
+    table: 'docente_por_formacao',
+    tableField: 'tipo_formacao',
+    resultField: 'formation_level',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'tipo_formacao'
+    }
+}).addValueToField({
+    name: 'adm_dependency',
+    table: '@',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}, 'filter') .addValue({
+    name: 'integral_time',
+    table: 'matricula',
+    tableField: 'tempo_integral',
+    resultField: 'integral_time_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'tempo_integral'
+    }
+}).addValue({
+    name: 'education_level_short',
+    table: 'matricula',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+});
+
+classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
+    let classSize = JSON.parse(req.body.class_size) || null;
+    let integralTime = JSON.parse(req.body.integral_time) || null;
+    let enrollmentProjection = (req.body.projections !== undefined) ? JSON.parse(req.body.projections) : null;
+
+    if(classSize == null || integralTime == null) {
+        res.statusCode = 400;
+        return res.json({err: {message: "There was an error processing class_size or integral_time. Check your JSON sintax and be sure you're sending both paramenters."}});
+    }
+    req.classSize = classSize;
+    req.integralTime = integralTime;
+    req.projections = (enrollmentProjection !== null) ? enrollmentProjection : false;
+
+    req.dims.state = true;
+    req.dims.city = true;
+    req.dims.school_year = true;
+    req.dims.location = true;
+
+    req.sql.field('sum(dia_total)', 'total_day')
+    .field('sum(noite_total)', 'total_night')
+    .field("'Brasil'", 'name')
+    .field('matricula_por_localizacao.ano_censo', 'year')
+    .from('matricula_por_localizacao')
+    .where('matricula_por_localizacao.serie_ano_id < 15')
+    .group('matricula_por_localizacao.ano_censo')
+    .order('matricula_por_localizacao.ano_censo')
+
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    req.enrollment = req.result;
+    
+    // Gera a relação etapa de ensino X ano escolar
+    req.educationSchoolYear = {};
+    for(let i = 1; i < 15; ++i) {
+        if(id2str.educationLevelSchoolYear(i) !== id2str.educationLevelSchoolYear(99)) {
+            let educationLevelId = (i > 10) ? Math.floor(i/10) : i;
+            
+            let classSize = req.classSize.find((el) => {return el.id === educationLevelId || el.id === i});
+            let integralTime = req.integralTime.find((el) => {return el.id === educationLevelId});
+            
+            let numberStudentClass = (typeof classSize !== 'undefined') ? classSize.numberStudentClass : null;
+            // Usa o offerGoal que é passado como parâmetro (Brasil inteiro). Atenção para isso.
+            let integralTimeOfferGoal = (typeof integralTime !== 'undefined') ? integralTime.offerGoal : null;
+            
+            let teacherByClass = (typeof classSize !== 'undefined') ? classSize.teacherByClass : null;
+
+            req.educationSchoolYear[i] = {
+                id: educationLevelId,
+                name: id2str.educationLevelShort(educationLevelId),
+                numberStudentClass,
+                integralTimeOfferGoal,
+                teacherByClass
+            };
+        }
+    }
+    
+    delete req.dims;
+    delete req.filter;
+    req.resetSql();
+    next();
+}, rqf.parse(), (req, res, next) => {
+
+    req.dims.state = true;
+    req.dims.city = true;
+    req.dims.location = true;
+    req.dims.school_building = true;
+
+    req.sql.field('SUM(escola.qtde_salas_utilizadas_dentro)', 'total')
+    .field("'Brasil'", 'name')
+    .field('escola.ano_censo', 'year')
+    .from('escola')
+    .group('escola.ano_censo')
+    .order('escola.ano_censo')
+    .where('escola.situacao_de_funcionamento = 1')
+    .where('escola.dependencia_adm_id < 4') 
+    .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1');
+
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) =>  {
+    req.classroom = req.result;
+
+    delete req.dims;
+    delete req.filter;
+    req.resetSql();
+    next();
+}, rqf.parse(), (req, res, next) => {
+    if (req.body.teacher_journey !== undefined) {
+        // req.teacherJourney = JSON.parse(req.body.teacher_journey) || null;
+
+        let schoolDays = JSON.parse(req.body.school_days) || null;
+        let teacherFormation = JSON.parse(req.body.teacher_formation) || null;
+        let teachingHours = JSON.parse(req.body.teaching_hours) || null;
+
+        let schoolDaysParsed = [];
+        let teachingHoursParsed = [];
+        for(let i = 1; i <= 6; i++) {   // Adciona turnos faltantes, corrige ordem dos id's
+            schoolDaysParsed.push(schoolDays.find((el) => {return el.id === i}))
+
+            let schoolYear = teachingHours.find((el) => {return el.id === i})
+            let shifts = []
+            for (let j = 1; j <= 3; j++) {
+                let turno = schoolYear.shifts.find((el) => {return el.id === j})
+                if (turno === undefined) {
+                    turno = {"id": j, value: 0}
+                }
+
+                shifts.push(turno)
+            }
+            schoolYear.shifts = shifts
+            teachingHoursParsed.push(schoolYear)
+        }
+
+        req.schoolDays = schoolDaysParsed;
+        req.teachingHours = teachingHoursParsed;
+
+        let teacherFormationParsed = []
+        for (let i of [2, 4, 6, 7, 8]) {
+            teacherFormationParsed.push(teacherFormation.find((el) => {return el.idFormationLevel === i}))
+        }
+        req.teacherFormation = teacherFormationParsed;
+        
+        req.teacherCalc = true;
+    }
+
+    req.dims.state = true;
+    req.dims.city = true;
+    req.dims.formation_level = true;
+    req.sql.field('count(distinct docente_por_formacao.id_docente)', 'total')
+           .field("'Brasil'", 'name')
+           .field('docente_por_formacao.ano_censo', 'year')
+           .from('docente_por_formacao')
+           .group('docente_por_formacao.ano_censo')
+           .order('docente_por_formacao.ano_censo');
+
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) =>  {
+    req.teacher = req.result;
+
+    delete req.dims;
+    delete req.filter;
+    req.resetSql();
+    next();
+}, rqf.parse(), (req, res, next) => {
+    req.dims.state = true;
+    req.dims.city = true;
+    req.dims.education_level_short = true;
+    req.dims.integral_time = true;
+    req.sql.field('COUNT(*)', 'total')
+           .field('matricula.ano_censo', 'year')
+           .from('matricula')
+           .group('matricula.ano_censo')
+           .order('matricula.ano_censo')
+           .where('((matricula.tipo<=3 OR matricula.tipo IS NULL) AND (matricula.tipo_atendimento_turma IS NULL OR matricula.tipo_atendimento_turma <= 2) AND matricula.turma_turno_id <> 99)');
+    next();
+}, rqf.build() ,query, id2str.transform(), (req, res, next) => {
+    // constrói objeto de tempo integral e calcula diagnósticos
+    var integral_time_result = req.result
+    req.integral_time = {}
+    for (let i = 0; i < integral_time_result.length; ++i){
+        // Se cidade não foi criada, cria
+        let integral_time = integral_time_result[i];
+        let code = '' + integral_time.year + integral_time.city_id
+        if (req.dims.school) code = code + integral_time.school_id
+
+        let currentCodeObj = req.integral_time[code]
+
+        // Cria município/escola caso não exista
+        if (currentCodeObj === undefined){
+            let obj = {
+                year: integral_time.year,
+                city_id: integral_time.city_id,
+                city_name: integral_time.city_name,
+                integral_time: {}
+            }
+            if (req.dims.school){
+                obj.school_id = integral_time.school_id
+                obj.school_name = integral_time.school_name
+            }
+            req.integral_time[code] = obj
+            currentCodeObj = obj;
+        }
+
+        // Pega objeto com dados do nível atual
+        var level = integral_time.education_level_short_id
+        var levelObj = currentCodeObj.integral_time[level]
+        if (levelObj === undefined){
+            levelObj = {
+                id: level,
+                integralTime: 0,
+                total: 0,
+            }
+        }
+
+        // Soma no total em integralTime, caso seja 1
+        levelObj.total += integral_time.total
+        if (integral_time.integral_time_id === 1){
+            levelObj.integralTime += integral_time.total
+        }
+
+        currentCodeObj.integral_time[level] = levelObj
+
+    }
+    for (let i in req.integral_time){
+        let curObj = req.integral_time[i]
+        // Adiciona diagnóstico aos objetos
+        for (let key in curObj.integral_time){
+            let cur = curObj.integral_time[key]
+            cur["diagnosis"] = cur.total ? (cur.integralTime/cur.total)*100.0 : 0.0;
+            curObj.integral_time[key] = cur
+        }
+    }
+
+    delete req.dims;
+    delete req.filter;
+    req.resetSql();
+    next()
+}, rqf.parse(), rqf.build(), (req, res, next) => {
+
+    // Talvez dê para remover todos os req.projections.
+    let enrollmentProjectionNight = [];
+    let enrollmentProjectionDay = [];
+    if (req.projections) {
+        for (let i = 0; i < req.projections.length; i++) {
+            if (req.projections[i].periods === 3) {
+                enrollmentProjectionNight.push(req.projections[i]);
+            } else {
+                enrollmentProjectionDay.push(req.projections[i]);
+            }
+        }
+    }
+    let qntYears = 1;
+    if (req.projections) {
+       qntYears = enrollmentProjectionDay[0].stagesEnrollments[0].seriesEnrollments[0].enrollments.length;
+    }
+    let result = [];
+
+    // Cria estrutura de resposta requisitada (itera por anos de projeção):
+    for(let yearCount = 0; yearCount < qntYears; yearCount++) {
+        let i = 0;
+        let j = 0;
+        let hashSet = new Set();
+        let enrollments = [...req.enrollment];
+
+        let ti = 0;     // index for teacher table
+
+        while (i < req.classroom.length) {
+            let classroom = req.classroom[i];
+            // Cria hash única para cada espacialidade, dado um ano
+            let hash = '' + yearCount + classroom.state_id + classroom.city_id; 
+            if (req.dims.school)
+                hash = hash + classroom.school_id
+            // Estrutura do objeto do resultado final
+            let obj = {
+                year: classroom.year + yearCount,
+                name: classroom.name,
+                state_id: classroom.state_id,
+                state_name: classroom.state_name,
+                city_id: classroom.city_id,
+                city_name: classroom.city_name,
+            };
+            if (req.dims.school){
+                obj.school_id = classroom.school_id,
+                obj.school_name = classroom.school_name
+            }
+            if (req.teacherCalc)
+                obj.percentage_teacher_career = [];
+            obj.locations = []
+
+            // Inserimos a localidade no array de locations da sala
+            let location = {
+                location_id: classroom.location_id,
+                location_name: classroom.location_name,
+                total_classroom: (classroom.school_building == 1) ? parseInt(classroom.total, 10) : 0,      // Conta apenas salas de prédios próprios
+                total_classroom_be_built: 0,
+                education_level: []
+            };
+            
+            // Ajusta estrutura de localização do último [município/escola] e inicializa variáveis para o atual
+            let currentClassroomObj = null;
+            if( !hashSet.has(hash) ) { // Nunca passou por esse município/escola
+                if (result[result.length - 1] !== undefined) {      // Após mudar de cidade, passamos pela cidade anterior e juntamos o valor to_be_built de localizações com o mesmo id
+                    let last_city = result[result.length - 1]
+                    let last_locations = result[result.length - 1].locations
+                    for (let i = 0; i < last_locations.length - 1; i++) {
+                        if (last_locations[i].location_id === last_locations[i+1].location_id) {
+                            last_locations[i].total_classroom_be_built += last_locations[i+1].total_classroom_be_built;
+                            last_locations[i].total_classroom += last_locations[i+1].total_classroom;
+                            last_locations.splice(i+1, 1);
+                        }
+                    }
+                    for (let i = 0; i < last_locations.length; i++) {   // Passamos agora para não deixar to_be_built < 0
+                        last_locations[i].total_classroom_be_built = (last_locations[i].total_classroom_be_built < 0) ? 0 : last_locations[i].total_classroom_be_built;
+                    }
+
+                    if (req.teacherCalc) executeTeacherCalc(last_city, ti);
+                }
+                hashSet.add(hash);
+                result.push(obj);
+                currentClassroomObj = obj;
+
+                var id_attribute = req.dims.school ? "school_id" : "city_id"
+                
+                var old_ti = ti;
+                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) // match da tabela de professores.
+                    ti++;
+                
+                if (ti === req.teacher.length) {
+                    console.log(classroom[id_attribute], "not found")
+                    while (classroom[id_attribute] === enrollments[j][id_attribute])
+                        enrollments.splice(j, 1)
+                    ti = old_ti; 
+                    i++;
+                    continue;
+                }
+
+            } else { // Se a hash já existe, já temos a cidade nos resultados. Como está ordenado, é o último valor nos resultados
+                currentClassroomObj = result[result.length - 1];
+            }
+            currentClassroomObj.locations.push(location);
+
+            // Partimos para as etapas de ensino/anos escolares
+            let enrollmentMatch = true;
+            j = 0;
+            let educationLevelSet = new Set();
+            let schoolYearSet = new Set();
+            let enrollment;
+            while(enrollmentMatch && j < enrollments.length) {
+                enrollment = enrollments[j];
+
+                if(typeof enrollment === 'undefined') {
+                    ++j;
+                    continue;
+                }
+                
+                if (req.dims.school){
+                    if(classroom.school_id !== enrollment.school_id) { // Se as escolas não são iguais, já passamos do range
+                        enrollmentMatch = false;
+                        continue;
+                    } 
+                }
+                else{
+                    if(classroom.city_id !== enrollment.city_id) { // Se as cidades não são iguais, já passamos do range
+                        enrollmentMatch = false;
+                        continue;
+                    } 
+                }
+
+                if(enrollment.year != classroom.year || enrollment.location_id != classroom.location_id) { // Se ano ou localização são diferentes, passa para o próximo
+                    ++j;
+                    continue;
+                }
+
+
+                // Temos uma matrícula com cidade/escola, ano e localidades certos
+                // "Consome" a matrícula (remove do vetor de matrículas)
+                enrollments.splice(j, 1);
+
+                // Cria a etapa de ensino adequada
+                let enrollmentEducationLevel = req.educationSchoolYear[enrollment.school_year_id];
+                // Se não há um número de alunos por turna para a etapa de ensino, ignoramos a entrada
+
+                if(enrollmentEducationLevel.numberStudentClass == null) continue;
+                
+                // Adiciona nível de educação para município/escola
+                let educationLevel = null;
+                if(!educationLevelSet.has(enrollmentEducationLevel.id)) { // cria e insere ordenadamente novo education level
+                    educationLevelSet.add(enrollmentEducationLevel.id);
+                    
+                    let itHash = '' + enrollment.year + enrollment.city_id
+                    if (req.dims.school) itHash += enrollment.school_id
+    
+                    let cur_education_level_short = enrollmentEducationLevel.id
+    
+                    let level_diagnosis = 0
+                    let integral_time = 0;
+                    let integral_time_total = 0;
+                    if (req.integral_time[itHash].integral_time[cur_education_level_short]){
+                        level_diagnosis = parseFloat( (req.integral_time[itHash].integral_time[cur_education_level_short].diagnosis).toFixed(2) )
+                        integral_time = req.integral_time[itHash].integral_time[cur_education_level_short].integralTime
+                        integral_time_total = req.integral_time[itHash].integral_time[cur_education_level_short].total
+                    }
+
+                    educationLevel = {
+                        education_level_short_id: enrollmentEducationLevel.id,
+                        education_level_short_name: enrollmentEducationLevel.name,
+                        enrollment: {
+                            integral_percentage: req.dims.school ? level_diagnosis : Math.max(level_diagnosis, enrollmentEducationLevel.integralTimeOfferGoal),
+                            integral_time: req.dims.school ? integral_time : Math.round(integral_time_total * Math.max(level_diagnosis, enrollmentEducationLevel.integralTimeOfferGoal)/100),
+                            integral_time_total: integral_time_total,
+                            total_enrollment_day: 0,
+                            total_enrollment_night: 0,
+                            full_period_classes: 0,
+                            day_classes: 0,
+                            night_classes: 0,
+                            total_classrooms_needed: 0
+                        }
+                    };
+
+                    if(enrollmentEducationLevel.id == 1) {
+                        educationLevel.classes_school_year = [];
+                    }
+
+                    // Para manter a ordem da etapa de ensino (insertion sort)
+                    if (location.education_level.length == 0) {
+                        location.education_level.push(educationLevel);
+                    } else {
+                        let k = location.education_level.length - 1;
+                        let el = location.education_level[k];
+                        while (k >= 0) {
+                            if(educationLevel.education_level_short_id < el.education_level_short_id) {
+                                --k;
+                                if(k>=0) el = location.education_level[k];
+                            } else break;
+                        }
+                        k++;
+                        location.education_level.splice(k, 0, educationLevel);
+                    }
+                } else { // pega o objeto de education level já existente
+                    let k = 0;
+                    let el = location.education_level[k];
+                    while(k < location.education_level.length) {
+                        if(el.education_level_short_id != enrollmentEducationLevel.id) {
+                            ++k;
+                            if(k<location.education_level.length) el = location.education_level[k];
+                        } else break;
+                    }
+                    if(k >= location.education_level.length) --k;
+                    educationLevel = location.education_level[k];
+                }
+
+                // Adiciona as séries da creche
+                let currentSchoolYear = null;
+                if(enrollmentEducationLevel.id == 1){
+                    let schoolYearHash = '' + enrollment.year + enrollment.city_id + enrollment.location_id + enrollment.school_year_id;
+                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.shcool_id
+
+                    if(schoolYearSet.has(schoolYearHash)) { // Busca a série escolar
+                        let k = 0;
+                        let el = educationLevel.classes_school_year[k];
+                        while(k < educationLevel.classes_school_year.length) {
+                            if(el.school_year_id != enrollment.school_year_id) {
+                                ++k;
+                                if(k < educationLevel.classes_school_year.length) el = educationLevel.classes_school_year[k];
+                            } else break;
+                        }
+                        if(k >= educationLevel.classes_school_year.length) --k;
+                        currentSchoolYear = educationLevel.classes_school_year[k];
+                    } else { // Adiciona uma nova série escolar
+                        let school_year = {
+                            school_year_id: enrollment.school_year_id,
+                            school_year_name: enrollment.school_year_name,
+                            total_enrollment_day: 0,
+                            total_enrollment_night: 0,
+                            full_period_classes: 0,
+                            day_classes: 0,
+                            night_classes: 0,
+                            total_classrooms_needed: 0
+                        }
+                        schoolYearSet.add(schoolYearHash);
+                        // Busca a posição para inserir
+                        let k = 0;
+                        let el = educationLevel.classes_school_year[k];
+                        while((typeof el !== 'undefined') && school_year.school_year_id > el.school_year_id) {
+                            el = educationLevel.classes_school_year[++k];
+                        }
+                        // educationLevel.classes_school_year.push(school_year);
+                        educationLevel.classes_school_year.splice(k, 0, school_year);
+                        currentSchoolYear = school_year;
+                    }
+                }
+
+                let currentIntegralOfferGoal = educationLevel.enrollment.integral_percentage;
+                
+                let currentNumberStudentClass = (enrollment.location_id == 1) ? enrollmentEducationLevel.numberStudentClass.urban : enrollmentEducationLevel.numberStudentClass.country;
+                
+                // Soma os totais de matrícula da etapa de ensino
+                educationLevel.enrollment.total_enrollment_day += enrollment.total_day;
+                educationLevel.enrollment.total_enrollment_night += (educationLevel.education_level_short_id > 2) ? enrollment.total_night : 0;      //Não contamos matrículas noturnos de pré-escola e creche
+
+                // Calcula o número de turmas parcial
+                // Turmas de período integral
+                educationLevel.enrollment.full_period_classes = parseFloat(((educationLevel.enrollment.total_enrollment_day * (currentIntegralOfferGoal/100)) / currentNumberStudentClass).toFixed(2));
+
+                // Turmas diurnas (matrículas diurnas - matrículas integrais)
+                educationLevel.enrollment.day_classes = parseFloat(((educationLevel.enrollment.total_enrollment_day * (1 - currentIntegralOfferGoal/100)) / currentNumberStudentClass).toFixed(2));
+
+                // Turmas noturnas
+                educationLevel.enrollment.night_classes = parseFloat((educationLevel.enrollment.total_enrollment_night / currentNumberStudentClass).toFixed(2)) || 0;
+
+                // Total de salas
+                educationLevel.enrollment.total_classrooms_needed = (educationLevel.enrollment.full_period_classes + educationLevel.enrollment.day_classes/2);
+
+                if(educationLevel.enrollment.night_classes > (educationLevel.enrollment.day_classes/2)) educationLevel.enrollment.total_classrooms_needed += (educationLevel.enrollment.night_classes - (educationLevel.enrollment.day_classes/2));
+
+                educationLevel.enrollment.total_classrooms_needed = parseFloat(educationLevel.enrollment.total_classrooms_needed.toFixed(2));
+
+                
+                let currentEnrollmentOfferDay;
+                let currentEnrollmentOfferNight;
+                if (req.projections) {
+                    currentEnrollmentOfferDay = enrollmentProjectionDay[enrollment.location_id - 1].stagesEnrollments[educationLevel.education_level_short_id - 1];
+                    currentEnrollmentOfferNight = enrollmentProjectionNight[enrollment.location_id - 1].stagesEnrollments[educationLevel.education_level_short_id - 1];
+                }
+
+                // Faz os mesmos cálculos para a série escolar
+                if(currentSchoolYear) {
+                    // Totais de matrícula
+                    currentSchoolYear.total_enrollment_day += enrollment.total_day;
+
+                    // Número de turmas parcial
+                    currentSchoolYear.full_period_classes = parseFloat(((currentSchoolYear.total_enrollment_day * (currentIntegralOfferGoal/100)) / currentNumberStudentClass).toFixed(2));
+
+                    currentSchoolYear.day_classes = parseFloat(((currentSchoolYear.total_enrollment_day * (1 - currentIntegralOfferGoal/100)) / currentNumberStudentClass).toFixed(2));
+
+
+                    // Total de salas
+                    currentSchoolYear.total_classrooms_needed = (currentSchoolYear.full_period_classes + currentSchoolYear.day_classes/2);
+
+                    currentSchoolYear.total_classrooms_needed = parseFloat(currentSchoolYear.total_classrooms_needed.toFixed(2));
+
+                    function reducer(key) { return (sum, elem) => sum + elem[key]}
+                    educationLevel.enrollment.total_enrollment_day = educationLevel.classes_school_year.reduce(reducer('total_enrollment_day'), 0);
+                    educationLevel.enrollment.full_period_classes = educationLevel.classes_school_year.reduce(reducer('full_period_classes'), 0);
+                    educationLevel.enrollment.day_classes = educationLevel.classes_school_year.reduce(reducer('day_classes'), 0);
+                    educationLevel.enrollment.total_classrooms_needed = educationLevel.classes_school_year.reduce(reducer('total_classrooms_needed'), 0);
+                }
+
+                enrollment = enrollments[j];
+            }
+
+            // Calculamos o total classroom be built para o município usando reduce
+            location.total_classroom_be_built = location.education_level.reduce((total, atual) => {
+                return total + atual.enrollment.total_classrooms_needed;
+            }, 0) - location.total_classroom;
+            ++i;
+        }
+
+        // Tratamento do último resultado, para remover double location, tirar negativo do to_be_built.
+        if (result[result.length - 1] !== undefined) {      // Após mudar de cidade, passamos pela cidade anterior e juntamos o valor to_be_built de localizações com o mesmo id
+            let last_city = result[result.length - 1]
+            let last_locations = result[result.length - 1].locations
+            for (let i = 0; i < last_locations.length - 1; i++) {
+                if (last_locations[i].location_id === last_locations[i+1].location_id) {
+                    last_locations[i].total_classroom_be_built += last_locations[i+1].total_classroom_be_built;
+                    last_locations[i].total_classroom += last_locations[i+1].total_classroom;
+                    last_locations.splice(i+1, 1);
+                }
+            }
+            for (let i = 0; i < last_locations.length; i++) {   // Passamos agora para não deixar to_be_built < 0
+                last_locations[i].total_classroom_be_built = (last_locations[i].total_classroom_be_built < 0) ? 0 : last_locations[i].total_classroom_be_built;
+            }
+
+            if (req.teacherCalc) executeTeacherCalc(last_city, ti);
+        }
+    }
+
+    // Agregar por estado e brasil
+    let reduction = null;
+    
+    if(req.dims.state || (!req.dims.city && !req.dims.school)) { // Se um dos dois acontecer, sabemos que devemos agregar
+        let i = 0;
+        reduction = [];
+        let reductionSet = new Set();
+        while (i < result.length) {
+            let city = result[i];
+            let obj = {
+                year: city.year,
+                name: city.name,
+                count: 1
+            }
+
+            if(req.dims.state) {
+                obj.state_id = city.state_id;
+                obj.state_name = city.state_name;
+            }
+            obj.percentage_teacher_career = []
+            obj.locations = [];
+            
+            let hash = '' + city.year;
+            if(req.dims.state) hash += '' + city.state_id;
+
+            let currentObj = null;
+            if(!reductionSet.has(hash)) {
+                reductionSet.add(hash);
+                reduction.push(obj);
+                currentObj = obj;
+            } else { // Está ordenado, podemos pegar o último
+                currentObj = reduction[reduction.length - 1];
+                currentObj.count++;
+            }
+            
+            if (currentObj.count == 1)
+                currentObj.percentage_teacher_career = city.percentage_teacher_career
+            else { 
+                // Incrementa valores percentuais de cada nivel de carreira no objeto atual
+                currentObj.percentage_teacher_career.forEach((item, ind, thisArr) => {
+                    thisArr[ind].percentage += city.percentage_teacher_career[ind].percentage
+                })
+            }
+
+            // Fazer "merge" do array locations da cidade com o da agregação
+            if(currentObj.locations.length == 0) {
+                // Pode ser que a cidade atual tenha menos localidades que o total (só urbana ou só rural)
+                currentObj.locations = [...city.locations];
+                if (currentObj.locations.length === 1) {        // Se a cidade só tinha uma, adcionamos a outra manualmente.
+                    currentObj.locations[1] = {
+                        location_id: (currentObj.locations[0].location_id === 1) ? 2 : 1,   // Oposto da adcionada
+                        location_name: (currentObj.locations[0].location_id === 1) ? 'Rural' : 'Urbana',    // Oposto da adcionada
+                        total_classroom: 0,
+                        total_classroom_be_built: 0,
+                        education_level: []
+                    }
+                }
+            } else {
+                let j = 0;
+                let k = 0;
+                let cityLocation = null;
+                let currentLocation = null;
+                while((typeof cityLocation !== 'undefined') && (typeof currentLocation !== 'undefined')) {
+                    cityLocation = city.locations[j];
+                    currentLocation = currentObj.locations[k];
+                    if(cityLocation.location_id < currentLocation.location_id) {
+                        ++j;
+                        cityLocation = city.locations[j];
+                        continue;
+                    } else if(cityLocation.location_id > currentLocation.location_id) {
+                        ++k;
+                        currentLocation = currentObj.locations[k];
+                        continue;
+                    }
+
+                    // Faz "merge" do array education_level
+                    // Se a localidade atual não tem o vetor
+                    if(currentLocation.education_level.length == 0) {
+                        currentLocation.education_level = [...cityLocation.education_level];
+                    } else { // Caso já tenha, atualiza os valores
+                        let l = 0;
+                        while(l < cityLocation.education_level.length) {
+                            let cityEducation = cityLocation.education_level[l];
+                            let m = 0;
+                            let currentEducation = currentLocation.education_level[m];
+                            while(m < currentLocation.education_level.length && cityEducation.education_level_short_id > currentEducation.education_level_short_id) {
+                                ++m;
+                                currentEducation = currentLocation.education_level[m];
+                            }
+                            if(m >= currentLocation.education_level.length) --m;
+                            currentEducation = currentLocation.education_level[m];
+
+                            if(currentEducation.education_level_short_id == cityEducation.education_level_short_id) {
+                                currentEducation.enrollment.integral_time += cityEducation.enrollment.integral_time;
+                                currentEducation.enrollment.integral_time_total += cityEducation.enrollment.integral_time_total;
+                                currentEducation.enrollment.total_enrollment_day += cityEducation.enrollment.total_enrollment_day;
+                                currentEducation.enrollment.total_enrollment_night += cityEducation.enrollment.total_enrollment_night;
+                                currentEducation.enrollment.full_period_classes += cityEducation.enrollment.full_period_classes;
+                                currentEducation.enrollment.day_classes += cityEducation.enrollment.day_classes;
+                                currentEducation.enrollment.night_classes += cityEducation.enrollment.night_classes;
+                                currentEducation.enrollment.total_classrooms_needed += cityEducation.enrollment.total_classrooms_needed;
+
+                                // Caso tenha feito o cálculo de professores, atualiza os valores
+                                if (req.teacherCalc) {
+                                    currentEducation.teacherNumber.total_teacher += cityEducation.teacherNumber.total_teacher;
+                                    currentEducation.teacherNumber.total_teacher_full_period += cityEducation.teacherNumber.total_teacher_full_period;
+                                    currentEducation.teacherNumber.total_teacher_partial += cityEducation.teacherNumber.total_teacher_partial;
+
+                                    currentEducation.teacherNumber.careerLevels.forEach((currentLevel, i) => {
+                                        currentLevel.total_teacher_career += cityEducation.teacherNumber.careerLevels[i].total_teacher_career;
+                                        currentLevel.total_teacher_full_period_career += cityEducation.teacherNumber.careerLevels[i].total_teacher_full_period_career;
+                                        currentLevel.total_teacher_partial_career += cityEducation.teacherNumber.careerLevels[i].total_teacher_partial_career;
+                                    })
+                                }
+
+                                // Insere as séries escolares, se existirem
+                                if((typeof cityEducation.classes_school_year !== 'undefined') && (typeof currentEducation.classes_school_year !== 'undefined')) {
+                                    let n = 0;
+                                    let o = 0;
+                                    let currentClass = currentEducation.classes_school_year[n];
+                                    let cityClass = cityEducation.classes_school_year[o];
+                                    while((typeof cityClass !== 'undefined') && (typeof currentClass !== 'undefined')) {
+                                        currentClass = currentEducation.classes_school_year[n];
+                                        cityClass = cityEducation.classes_school_year[o];
+
+                                        // Se a série escolar é menor que a atual, ela não está no vetor, pois o vetor está ordenado e tem range limitado
+                                        if(cityClass.school_year_id < currentClass.school_year_id) {
+                                            currentEducation.classes_school_year.splice(n, 0, cityClass);
+                                            currentClass = currentEducation.classes_school_year[n];
+                                            cityClass = cityEducation.classes_school_year[++o];
+                                            continue;
+                                        } else if(cityClass.school_year_id > currentClass.school_year_id) {
+                                            currentClass = currentEducation.classes_school_year[++n];
+                                            // Se o ano escolar da cidade é  maior que do objeto atual E o vetor de ano escolar do objeto atual
+                                            // acaba, então este ano escolar falta no objeto atual, pois os anos escolares estão ordenados
+                                            if((typeof currentClass == 'undefined') && (typeof cityClass !== 'undefined')) {
+                                                currentEducation.classes_school_year[n] = cityClass;
+                                                currentClass = currentEducation.classes_school_year[n];
+                                            }
+                                            continue;
+                                        }
+
+                                        currentClass.total_enrollment_day += cityClass.total_enrollment_day;
+                                        currentClass.total_enrollment_night += cityClass.total_enrollment_night;
+                                        currentClass.full_period_classes += cityClass.full_period_classes;
+                                        currentClass.day_classes += cityClass.day_classes;
+                                        currentClass.night_classes += cityClass.night_classes;
+                                        currentClass.total_classrooms_needed += cityClass.total_classrooms_needed;
+
+                                        // Caso tenha feito o cálculo de professores, atualiza os valores
+                                        if (req.teacherCalc) {
+                                            currentClass.teacherNumber.total_teacher += cityClass.teacherNumber.total_teacher;
+                                            currentClass.teacherNumber.total_teacher_full_period += cityClass.teacherNumber.total_teacher_full_period;
+                                            currentClass.teacherNumber.total_teacher_partial += cityClass.teacherNumber.total_teacher_partial;
+
+                                            // currentClass.teacherNumber.careerLevels.forEach((currentLevel, i) => {
+                                            //     currentLevel.total_teacher_career += cityClass.teacherNumber.careerLevels[i].total_teacher_career;
+                                            //     currentLevel.total_teacher_full_period_career += cityClass.teacherNumber.careerLevels[i].total_teacher_full_period_career;
+                                            //     currentLevel.total_teacher_partial_career += cityClass.teacherNumber.careerLevels[i].total_teacher_partial_career;
+                                            // })
+                                        }
+    
+                                        cityClass = cityEducation.classes_school_year[++o];
+                                    }
+
+                                }
+                            } else {
+                                if(currentEducation.education_level_short_id < cityEducation.education_level_short_id) {
+                                    currentLocation.education_level.splice(++m, 0, cityEducation);
+                                } else {
+                                    currentLocation.education_level.splice(m, 0, cityEducation);
+                                }
+                            }
+                            ++l;
+                        }
+                    }
+                    // Reinicia vetor percentage_teacher_career e calcula porcentagens pelos totais
+
+                    currentLocation.total_classroom += cityLocation.total_classroom;
+                    currentLocation.total_classroom_be_built += cityLocation.total_classroom_be_built;
+
+                    ++j;
+                    cityLocation = city.locations[j];
+                }
+            }
+            ++i;
+        }
+        for (let state of reduction){
+            state.percentage_teacher_career.forEach((item, ind, thisArr) => {
+                thisArr[ind].percentage = Number((thisArr[ind].percentage / state.count).toFixed(2))
+            })
+            delete state.count
+            for (let location of state.locations){
+                for (let educationLevel of location.education_level){
+                    let total = educationLevel.enrollment.integral_time_total;
+                    let integralTime = educationLevel.enrollment.integral_time;
+                    educationLevel.enrollment.integral_percentage = total ? parseFloat( (100*(integralTime / total)).toFixed(2) ) : 0       
+                }
+            }
+        }
+    }
+
+    req.result = reduction || result;
+
+
+    function executeTeacherCalc(lastCity, index) {
+        let lastLocations = lastCity.locations
+        let teacherByFormation = [];        // Vetor com a porcentagem de professores por formação.
+        let teacherTotal = req.teacher[index].total;
+        
+        let i = index + 1;
+        for (let j of [2, 4, 6, 7, 8]) {       // Calcula a porcentagem de professores por formação.
+            if (req.teacher[i] === undefined || j !== req.teacher[i].formation_level) {         // Nesse caso a cidade não possuí professores com a formação atual, adcionamos 0.
+                teacherByFormation.push(0);
+            }
+            else {
+                teacherByFormation.push(parseFloat((req.teacher[i].total / teacherTotal).toFixed(3)));
+                i++;
+            }
+        }
+
+        // verifica se a soma de porcentagens vale 100.
+        let sum = 0;
+        for (let value of teacherByFormation) {
+            sum += value;
+        }
+        teacherByFormation[1] += teacherByFormation[0]
+        teacherByFormation[0] = 0
+
+        let diff = 1 - sum;
+        
+        // Se soma de porcentagens for menor/maior que 100, faz correção
+        if (Math.abs(diff) > 0.0001) {
+            // Garante que a porcentagem corrigida não ficará negativa
+            let indDiff = 1;
+            while (teacherByFormation[indDiff] + diff < 0) indDiff++;
+            teacherByFormation[indDiff] += diff;
+        }
+
+        // Cria vetor de porcentagens de carreira dos professores
+        req.teacherFormation.forEach((formation, i) => {
+            lastCity.percentage_teacher_career.push({
+                formation_level_id: formation.idFormationLevel,
+                percentage: Number((teacherByFormation[i]*100).toFixed(2)),
+            })
+        });
+
+        lastLocations.forEach((location) => {
+            location.education_level.forEach((educationLevel) => {
+                let educationLevelId = educationLevel.education_level_short_id;
+
+                let currentTeachingHours = req.teachingHours[educationLevelId-1].shifts;
+        
+                if(educationLevelId === 1) {        // Devido a divisão da creche é necessário tratá-la separadamente.
+                    educationLevel.classes_school_year.forEach((schoolYear) => {        // Aplicamos os cálculos para os anos da creche
+                        let teachingTimeFullPeriod = schoolYear.full_period_classes * currentTeachingHours[2].value * req.schoolDays[educationLevelId-1].value;
+                        let teachingTimeNight = schoolYear.night_classes * currentTeachingHours[1].value * req.schoolDays[educationLevelId-1].value;
+                        let teachingTimeDay = schoolYear.day_classes * currentTeachingHours[0].value * req.schoolDays[educationLevelId-1].value;
+
+                        let currentTeacherByClass = (location.location_id === 1) ? req.educationSchoolYear[schoolYear.school_year_id].teacherByClass.urban : req.educationSchoolYear[schoolYear.school_year_id].teacherByClass.country;
+                        
+                        let numberOfTeacherFullPeriod = 0;
+                        let numberOfTeacherNight = 0;
+                        let numberOfTeacherDay = 0;
+                        lastCity.percentage_teacher_career.forEach(career => {
+                            let journeyObj = req.teacherFormation.find(formation => formation.idFormationLevel === career.formation_level_id)
+                            let journey = journeyObj.journeyWithInteraction/100 * journeyObj.journeyTotal;
+                            numberOfTeacherFullPeriod += (teachingTimeFullPeriod / journey) * currentTeacherByClass * career.percentage/100;
+                            numberOfTeacherNight += (teachingTimeNight / journey) * currentTeacherByClass * career.percentage/100;
+                            numberOfTeacherDay += (teachingTimeDay / journey) * currentTeacherByClass * career.percentage/100;
+                        })
+                        numberOfTeacherFullPeriod = parseFloat(numberOfTeacherFullPeriod.toFixed(2));
+                        numberOfTeacherNight = parseFloat(numberOfTeacherNight.toFixed(2));
+                        numberOfTeacherDay = parseFloat(numberOfTeacherDay.toFixed(2));
+
+                        schoolYear.teacherNumber = {
+                            total_teacher : numberOfTeacherDay + numberOfTeacherNight + numberOfTeacherFullPeriod,
+                            total_teacher_full_period : numberOfTeacherFullPeriod,
+                            total_teacher_partial : numberOfTeacherNight + numberOfTeacherDay
+                        }
+                    })
+
+                    // Calculamos para o educationLevel usando reduce
+                    function reducer(key) { return (sum, elem) => sum + elem.teacherNumber[key]}
+                    educationLevel.teacherNumber = {
+                        total_teacher : educationLevel.classes_school_year.reduce(reducer('total_teacher'), 0),
+                        total_teacher_full_period : educationLevel.classes_school_year.reduce(reducer('total_teacher_full_period'), 0),
+                        total_teacher_partial : educationLevel.classes_school_year.reduce(reducer('total_teacher_partial'), 0)
+                    }
+
+                    // function reducerList(idx, key) { return (sum, elem) => sum + elem.teacherNumber.careerLevels[idx][key]}
+                    educationLevel.teacherNumber.careerLevels = [];
+                    req.teacherFormation.forEach((formation, i) => {
+                        let totalTeacherFullPeriodCareer = educationLevel.teacherNumber.total_teacher_full_period * teacherByFormation[i];
+                        let totalTeacherPartialCareer = educationLevel.teacherNumber.total_teacher_partial * teacherByFormation[i];
+
+                        educationLevel.teacherNumber.careerLevels.push({
+                            sequence: formation.sequence,
+                            denomination: formation.denomination,
+                            formation_level_id: formation.idFormationLevel,
+                            total_teacher_career: totalTeacherFullPeriodCareer + totalTeacherPartialCareer,
+                            total_teacher_full_period_career: totalTeacherFullPeriodCareer,
+                            total_teacher_partial_career:totalTeacherPartialCareer,
+                        })
+                    })
+                }
+
+                else {
+                    let teachingTimeFullPeriod = educationLevel.enrollment.full_period_classes * currentTeachingHours[2].value * req.schoolDays[educationLevelId-1].value;
+                    let teachingTimeNight = educationLevel.enrollment.night_classes * currentTeachingHours[1].value * req.schoolDays[educationLevelId-1].value;
+                    let teachingTimeDay = educationLevel.enrollment.day_classes * currentTeachingHours[0].value * req.schoolDays[educationLevelId-1].value;
+
+                    let currentTeacherByClass = (location.location_id === 1) ? req.educationSchoolYear[educationLevelId].teacherByClass.urban : req.educationSchoolYear[educationLevelId].teacherByClass.country;
+        
+                    let numberOfTeacherFullPeriod = 0;
+                    let numberOfTeacherNight = 0;
+                    let numberOfTeacherDay = 0;
+                    lastCity.percentage_teacher_career.forEach(career => {
+                        let journeyObj = req.teacherFormation.find(formation => formation.idFormationLevel === career.formation_level_id)
+                        let journey = journeyObj.journeyWithInteraction/100 * journeyObj.journeyTotal;
+                        numberOfTeacherFullPeriod += (teachingTimeFullPeriod / journey) * currentTeacherByClass * career.percentage/100;
+                        numberOfTeacherNight += (teachingTimeNight / journey) * currentTeacherByClass * career.percentage/100;
+                        numberOfTeacherDay += (teachingTimeDay / journey) * currentTeacherByClass * career.percentage/100;
+                    })
+                    numberOfTeacherFullPeriod = parseFloat(numberOfTeacherFullPeriod.toFixed(2));
+                    numberOfTeacherNight = parseFloat(numberOfTeacherNight.toFixed(2));
+                    numberOfTeacherDay = parseFloat(numberOfTeacherDay.toFixed(2));
+
+                    educationLevel.teacherNumber = {
+                        total_teacher : numberOfTeacherDay + numberOfTeacherNight + numberOfTeacherFullPeriod,
+                        total_teacher_full_period : numberOfTeacherFullPeriod,
+                        total_teacher_partial : numberOfTeacherNight + numberOfTeacherDay
+                    }
+
+                    educationLevel.teacherNumber.careerLevels = [];
+                    req.teacherFormation.forEach((formation, i) => {
+                        let totalTeacherFullPeriodCareer = educationLevel.teacherNumber.total_teacher_full_period * teacherByFormation[i];
+                        let totalTeacherPartialCareer = educationLevel.teacherNumber.total_teacher_partial * teacherByFormation[i];
+
+                        educationLevel.teacherNumber.careerLevels.push({
+                            sequence: formation.sequence,
+                            denomination: formation.denomination,
+                            formation_level_id: formation.idFormationLevel,
+                            total_teacher_career: totalTeacherFullPeriodCareer + totalTeacherPartialCareer,
+                            total_teacher_full_period_career: totalTeacherFullPeriodCareer,
+                            total_teacher_partial_career:totalTeacherPartialCareer,
+                        })
+                    })
+
+                }
+            })
+        })
+    }
+
+    next();
+}, response('classroom_count'));
+
+module.exports = classroomCountApp;
+
diff --git a/src/libs/routes_v2/courseCount.js b/src/libs/routes_v2/courseCount.js
new file mode 100644
index 00000000..404ed2eb
--- /dev/null
+++ b/src/libs/routes_v2/courseCount.js
@@ -0,0 +1,814 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+const { join } = require('lodash');
+
+const courseCountApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+let rqf = new ReqQueryFields();
+
+let rqfMapfor = new ReqQueryFields();
+
+courseCountApp.get('/upper_adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 7; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.upperAdmDependency(i)
+        });
+    };
+    next();
+}, response('upper_adm_dependency'));
+
+courseCountApp.get('/years', (req, res, next) => {
+    req.sql.from('curso_ens_superior')
+    .field('DISTINCT curso_ens_superior.ano_censo', 'year')
+    .where('curso_ens_superior.ano_censo > 2010');
+    next();
+}, query, response('years'));
+
+courseCountApp.get('/year_range', (req, res, next) => {
+    req.sql.from('curso_ens_superior')
+    .field('MIN(curso_ens_superior.ano_censo)', 'start_year')
+    .field('MAX(curso_ens_superior.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+courseCountApp.get('/academic_organization', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.academicOrganization(i)
+        });
+    };
+    next();
+}, response('academic_organization'));
+
+courseCountApp.get('/ocde_geral', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 8; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ocdeGeral(i)
+        });
+    };
+    next();
+}, response('ocde_geral'));
+
+courseCountApp.get('/ocde_specific', (req, res, next) => {
+    req.result = [];
+    const defaultCase = null;
+    for(let i = 1; i <= 86; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.ocdeSpecific(i)
+        };
+        if (obj.name !== id2str.ocdeSpecific(defaultCase)){
+            req.result.push(obj);
+        }
+    };
+    req.result.push({
+        id: defaultCase,
+        name: id2str.ocdeSpecific(defaultCase)
+    });
+    next();
+}, response('ocde_specific'));
+
+courseCountApp.get('/ocde_detailed', (req, res, next) => {
+    req.result = [];
+    const defaultCase = null;
+    for(let i = 142; i <= 863; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.ocdeDetailed(i)
+        };
+        if (obj.name !== id2str.ocdeDetailed(defaultCase)){
+            req.result.push(obj);
+        }
+    };
+    req.result.push({
+        id: defaultCase,
+        name: id2str.ocdeDetailed(defaultCase)
+    });
+    next();
+}, response('ocde_detailed'));
+
+courseCountApp.get('/cine_geral', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 10; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.cineGeral(i)
+        });
+    };
+    next();
+}, response('cine_geral'));
+
+courseCountApp.get('/cine_specific', (req, res, next) => {
+    req.result = [];
+    const defaultCase = null;
+    for(let i = 1; i <= 104; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.cineSpecific(i)
+        };
+        if (obj.name !== id2str.cineSpecific(defaultCase)){
+            req.result.push(obj);
+        }
+    };
+    req.result.push({
+        id: defaultCase,
+        name: id2str.cineSpecific(defaultCase)
+    });
+    next();
+}, response('cine_specific'));
+
+courseCountApp.get('/cine_detailed', (req, res, next) => {
+    req.result = [];
+    const defaultCase = null;
+    for(let i = 11; i <= 1041; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.cineDetailed(i)
+        };
+        if (obj.name !== id2str.cineDetailed(defaultCase)){
+            req.result.push(obj);
+        }
+    };
+    req.result.push({
+        id: defaultCase,
+        name: id2str.cineDetailed(defaultCase)
+    });
+    next();
+}, response('cine_detailed'));
+
+courseCountApp.get('/academic_level', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.academicLevel(i)
+        });
+    };
+    next();
+}, response('academic_level'));
+
+courseCountApp.get('/upper_education_mod', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 2; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.upperEducationMod(i)
+        });
+    };
+    next();
+}, response('upper_education_mod'));
+
+courseCountApp.get('/is_free', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Classificado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('is_free'));
+
+courseCountApp.get('/night_time', (req, res, next) => {
+    req.result = [{
+        id: 9, 
+        name: id2str.nightTime(9)
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.nightTime(i)
+        })
+    }
+    next();
+}, response('night_time'));
+
+courseCountApp.get('/university', (req, res, next) => {
+    req.sql.from('curso_ens_superior')
+    .field('DISTINCT curso_ens_superior.nome_ies', 'nome')
+    .field('curso_ens_superior.cod_ies', 'cod')
+    next();
+}, query, response('university'));
+
+courseCountApp.get('/localoffer', (req, res, next) => {
+    req.sql.from('localoferta_ens_superior', 'l')
+    .field('DISTINCT l.nome', 'localoffer_name')
+    .field('l.cod_local_oferta', 'localoffer_id');
+    next();
+}, query, response('localoffer'));
+
+
+rqfMapfor.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValueToField({
+    name: 'state',
+    table: 'municipio',
+    tableField: 'estado_id',
+    resultField: 'state_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+	    table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio',
+        foreignTable: 'localoferta_ens_superior'
+    }
+}, 'filter').addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio',
+        foreignTable: 'localoferta_ens_superior'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio',
+        foreignTable: 'localoferta_ens_superior'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['id', 'nome'],
+    resultField: ['city_id', 'city_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio',
+        foreignTable: 'localoferta_ens_superior'
+    }
+}).addValue({
+    name:'academic_level',
+    table: 'curso_ens_superior',
+    tableField: 'cod_grau_academico',
+    resultField: 'academic_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_grau_academico'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'localoferta_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'localoferta_ens_superior',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'localoferta_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'localoferta_ens_superior',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name:'course',
+    table: 'curso_ens_superior',
+    tableField: 'nome_curso',
+    resultField: 'course_name',
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'nome_curso'
+    }
+}).addValueToField({
+    name: 'campi',
+    table: 'localoferta_ens_superior',
+    tableField: ['cod_local_oferta', 'nome'],
+    resultField: ['campi_id', 'campi_name'],
+    where: {
+	    relation: '=',
+	    type: 'integer',
+	    field: 'cod_local_oferta',
+	    table: 'localoferta_ens_superior'
+    }
+}, 'filter').addValue({
+    name:'upper_adm_dependency',
+    table: 'curso_ens_superior',
+    tableField: 'par_categoria_administrativa',
+    resultField: 'upper_adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_categoria_administrativa'
+    }
+}).addValue({
+    name:'upper_education_mod',
+    table: 'curso_ens_superior',
+    tableField: 'cod_modalidade_ensino',
+    resultField: 'upper_education_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_modalidade_ensino'
+    }
+}).addValue({
+    name:'academic_organization',
+    table: 'curso_ens_superior',
+    tableField: 'cod_organizacao_academica',
+    resultField: 'academic_organization_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_organizacao_academica'
+    }
+})
+
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'region',
+    table: 'localoferta_ens_superior',
+    tableField: ['nome_regiao', 'cod_regiao'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_regiao',
+	    table: 'localoferta_ens_superior'
+    },
+    join: {
+        primary: ['ano_censo', 'cod_curso'],
+        foreign: ['ano_censo', 'cod_curso'],
+        foreignTable: 'curso_ens_superior'
+    }
+}).addValue({
+    name: 'state',
+    table: 'localoferta_ens_superior',
+    tableField: ['sigla_uf_t', 'cod_uf_t'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_uf',
+	    table: 'localoferta_ens_superior'
+    },
+    join: {
+        primary: ['ano_censo', 'cod_curso'],
+        foreign: ['ano_censo', 'cod_curso'],
+        foreignTable: 'curso_ens_superior'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio_curso',
+        foreignTable: 'curso_ens_superior'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio_curso',
+        foreignTable: 'curso_ens_superior'
+    }
+}).addValue({
+    name: 'city',
+    table: 'localoferta_ens_superior',
+    tableField: ['cod_municipio_t', 'nome_municipio_t'],
+    resultField: ['city_id', 'city_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_municipio',
+        table: 'localoferta_ens_superior'
+    },
+    join: {
+        primary: ['ano_censo', 'cod_curso'],
+        foreign: ['ano_censo', 'cod_curso'],
+        foreignTable: 'curso_ens_superior'
+    }
+}).addValue({
+    name: 'localoffer',
+    table: 'localoferta_ens_superior',
+    tableField: ['cod_local_oferta', 'nome'],
+    resultField: ['localoffer_id', 'localoffer_name'],
+    where: {
+	    relation: '=',
+	    type: 'integer',
+	    field: 'cod_curso',
+	    table: 'curso_ens_superior'
+    },
+    join: {
+        primary: ['ano_censo', 'cod_curso'],
+	    foreign: ['ano_censo', 'cod_curso'],
+	    foreignTable: 'curso_ens_superior'
+    }
+}).addValue({
+    name: 'campi',
+    table: 'localoferta_ens_superior',
+    tableField: ['cod_local_oferta', 'nome'],
+    resultField: ['campi_id', 'campi_name'],
+    where: {
+	    relation: '=',
+	    type: 'integer',
+	    field: 'cod_curso',
+	    table: 'curso_ens_superior'
+    },
+    join: {
+        primary: ['ano_censo', 'cod_curso'],
+	    foreign: ['ano_censo', 'cod_curso'],
+	    foreignTable: 'curso_ens_superior'
+    }
+}).addValue({
+    name: 'university',
+    table: 'curso_ens_superior',
+    tableField: ['cod_ies', 'nome_ies'],
+    resultField: ['university_id', 'university_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ies'
+    }
+}).addValue({
+    name: 'universityLocalOffer',
+    table: 'curso_ens_superior',
+    tableField: ['cod_ies', 'nome_ies'],
+    resultField: ['university_id', 'university_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ies'
+    }
+}).addValue({
+    name:'upper_adm_dependency',
+    table: 'curso_ens_superior',
+    tableField: 'par_categoria_administrativa',
+    resultField: 'upper_adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_categoria_administrativa'
+    }
+}).addValue({
+    name:'academic_organization',
+    table: 'curso_ens_superior',
+    tableField: 'cod_organizacao_academica',
+    resultField: 'academic_organization_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_organizacao_academica'
+    }
+}).addValue({
+    name:'course',
+    table: 'curso_ens_superior',
+    tableField: 'nome_curso',
+    resultField: 'course_name',
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'nome_curso'
+    }
+}).addValue({
+    name:'ocde_specific',
+    table: 'curso_ens_superior',
+    tableField: ['cod_ocde_area_especifica', 'nome_ocde_area_especifica'],
+    resultField: ['ocde_specific_id', 'ocde_specific_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ocde_area_especifica'
+    }
+}).addValue({
+    name:'ocde_geral',
+    table: 'curso_ens_superior',
+    tableField: ['cod_ocde_area_geral', 'nome_ocde_area_geral'],
+    resultField: ['ocde_geral_id', 'ocde_geral_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ocde_area_geral'
+    }
+}).addValue({
+    name:'ocde_detailed',
+    table: 'curso_ens_superior',
+    tableField: ['cod_ocde_area_detalhada', 'nome_ocde_area_detalhada'],
+    resultField: ['ocde_detailed_id', 'ocde_detailed_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ocde_area_detalhada'
+    }
+}).addValue({
+    name:'cine_specific',
+    table: 'curso_ens_superior',
+    tableField: ['cod_cine_area_especifica', 'nome_cine_area_especifica'],
+    resultField: ['cine_specific_id', 'cine_specific_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_cine_area_especifica'
+    }
+}).addValue({
+    name:'cine_geral',
+    table: 'curso_ens_superior',
+    tableField: ['cod_cine_area_geral', 'nome_cine_area_geral'],
+    resultField: ['cine_geral_id', 'cine_geral_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_cine_area_geral'
+    }
+}).addValue({
+    name:'cine_detailed',
+    table: 'curso_ens_superior',
+    tableField: ['cod_cine_area_detalhada', 'nome_cine_area_detalhada'],
+    resultField: ['cine_detailed_id', 'cine_detailed_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_cine_area_detalhada'
+    }
+}).addValue({
+    name:'academic_level',
+    table: 'curso_ens_superior',
+    tableField: 'cod_grau_academico',
+    resultField: 'academic_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_grau_academico'
+    }
+}).addValue({
+    name:'upper_education_mod',
+    table: 'curso_ens_superior',
+    tableField: 'cod_modalidade_ensino',
+    resultField: 'upper_education_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_modalidade_ensino'
+    }
+}).addValue({
+    name:'is_free',
+    table: 'curso_ens_superior',
+    tableField: 'gratuito',
+    resultField: 'is_free_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'gratuito'
+    }
+}).addValue({
+    name:'night_time',
+    table: 'curso_ens_superior',
+    tableField: 'noturno_curso_t',
+    resultField: 'night_time_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'noturno_curso_t'
+    }
+}).addValue({
+    name:'situation',
+    table: 'curso_ens_superior',
+    tableField: 'cod_situacao_curso',
+    resultField: 'situacao_curso_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_situacao_curso'
+    }
+}).addValue({
+    name:'year',
+    table: 'curso_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'curso_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'curso_ens_superior',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'curso_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'curso_ens_superior',
+        field: 'ano_censo'
+    }
+})
+
+courseCountApp.get('/count_by_name', rqfMapfor.parse(), (req, res, next) => {
+    req.sql.from('localoferta_ens_superior')
+    .field('COUNT(DISTINCT localoferta_ens_superior.cod_curso)', 'total')
+    .field('localoferta_ens_superior.ano_censo', 'year')
+    .join ('curso_ens_superior ON (localoferta_ens_superior.cod_curso = curso_ens_superior.cod_curso) AND (localoferta_ens_superior.ano_censo = curso_ens_superior.ano_censo)')
+    .where('curso_ens_superior.cod_nivel_academico = 1')
+    .where('curso_ens_superior.tipo_atributo_ingresso <> 1 OR curso_ens_superior.tipo_atributo_ingresso is NULL')
+    .group('localoferta_ens_superior.ano_censo')
+    .order('localoferta_ens_superior.ano_censo')
+    
+    next();
+}, rqfMapfor.build(), query, (req, res, next) =>{
+    if ('course' in req.dims){
+        var total_course = req.result.reduce((total, cur) => {return total += cur.total}, 0)
+        for (var course of req.result){
+            course.percentage = Number((( course.total / total_course ) * 100).toFixed(2))
+        }
+    }
+    next();
+}, id2str.transform(), response('count_by_name'));
+
+courseCountApp.get('/', rqf.parse(), (req, res, next) => {
+    if ("localoffer" in req.dims || "campi" in req.dims) {
+        if ("university" in req.dims || "universityLocalOffer" in req.dims) {
+            req.sql.from('curso_ens_superior')
+                .field('curso_ens_superior.ano_censo', 'year')
+                .field('COUNT(localoferta_ens_superior.cod_local_oferta)', 'total')
+                .where('curso_ens_superior.tipo_atributo_ingresso <> 1 OR curso_ens_superior.tipo_atributo_ingresso is NULL')
+                .where('curso_ens_superior.cod_nivel_academico = 1')
+                .group('curso_ens_superior.ano_censo')
+                .order('curso_ens_superior.ano_censo')
+                .order('localoferta_ens_superior.cod_local_oferta');
+        } else {
+            req.sql.from('curso_ens_superior')
+                .field('curso_ens_superior.ano_censo', 'year')
+                .field('COUNT(localoferta_ens_superior.cod_local_oferta)', 'total')
+                .field('localoferta_ens_superior.cod_ies', 'university_id')
+                .field('curso_ens_superior.nome_ies', 'university_name')
+                .where('curso_ens_superior.tipo_atributo_ingresso <> 1 OR curso_ens_superior.tipo_atributo_ingresso is NULL')
+                .where('curso_ens_superior.cod_nivel_academico = 1')
+                .group('curso_ens_superior.ano_censo')
+                .group('localoferta_ens_superior.cod_ies')
+                .group('curso_ens_superior.nome_ies')
+                .order('curso_ens_superior.ano_censo')
+                .order('localoferta_ens_superior.cod_local_oferta');
+        }
+    } else if (("state" in req.dims) || ("city" in req.dims) || ("microregion" in req.dims) || ("mesoregion" in req.dims) || ("region" in req.dims) || 
+        ("state" in req.filter) || ("city" in req.filter) || ("microregion" in req.filter) || ("mesoregion" in req.filter) || ("region" in req.filter) ) {
+        if ("course" in req.dims){
+            req.sql.from('curso_ens_superior')
+                .field('COUNT(*)', 'total')
+                .field("'Brasil'", 'name')
+                .field('curso_ens_superior.ano_censo', 'year')
+                .group('curso_ens_superior.ano_censo')
+                .order('curso_ens_superior.ano_censo')
+                .where('curso_ens_superior.cod_nivel_academico = 1')
+        }
+        else{
+            req.sql.from('curso_ens_superior')
+                .field('COUNT(DISTINCT curso_ens_superior.cod_curso)', 'total')
+                .field("'Brasil'", 'name')
+                .field('curso_ens_superior.ano_censo', 'year')
+                .group('curso_ens_superior.ano_censo')
+                .order('curso_ens_superior.ano_censo')
+                .where('curso_ens_superior.tipo_atributo_ingresso <> 1 OR curso_ens_superior.tipo_atributo_ingresso is NULL')
+                .where('curso_ens_superior.cod_nivel_academico = 1');
+        }
+        
+    } else if ("university" in req.dims || "universityLocalOffer" in req.dims) {
+        req.sql.from('curso_ens_superior')
+            .field('COUNT(curso_ens_superior.cod_curso)', 'total')
+            .field("'Brasil'", 'name')
+            .field('curso_ens_superior.ano_censo', 'year')
+            .group('curso_ens_superior.cod_ies')
+            .group('curso_ens_superior.ano_censo')
+            .order('curso_ens_superior.cod_ies')
+            .order('curso_ens_superior.ano_censo')
+            .where('curso_ens_superior.tipo_atributo_ingresso <> 1 OR curso_ens_superior.tipo_atributo_ingresso is NULL')
+            .where('curso_ens_superior.cod_nivel_academico = 1');
+    } else {
+        req.sql.from('curso_ens_superior')
+            .field('COUNT(curso_ens_superior.cod_curso)', 'total')
+            .field("'Brasil'", 'name')
+            .field('curso_ens_superior.ano_censo', 'year')
+            .group('curso_ens_superior.ano_censo')
+            .order('curso_ens_superior.ano_censo')
+            .where('curso_ens_superior.tipo_atributo_ingresso <> 1 OR curso_ens_superior.tipo_atributo_ingresso is NULL')
+            .where('curso_ens_superior.cod_nivel_academico = 1');
+    }
+    next();
+}, rqf.build(), query, (req, res, next) =>{
+    if ('course' in req.dims){
+        var total_course = req.result.reduce((total, cur) => {return total += cur.total}, 0)
+        for (var course of req.result){
+            course.percentage = Number((( course.total / total_course ) * 100).toFixed(2))
+        }
+    }
+    next();
+}, id2str.transform(), addMissing(rqf), response('course_count'));
+
+module.exports = courseCountApp;
diff --git a/src/libs/routes_v2/courseStudents.js b/src/libs/routes_v2/courseStudents.js
new file mode 100644
index 00000000..77180cb2
--- /dev/null
+++ b/src/libs/routes_v2/courseStudents.js
@@ -0,0 +1,201 @@
+const express = require('express');
+
+const courseStudentsApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+let rqf = new ReqQueryFields();
+
+courseStudentsApp.get('/enrolled_vacancies_freshmen', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 3; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.enrolledVacanciesFreshmen(i)
+        });
+    };
+    next();
+}, response('enrolled_vacancies_freshmen'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+})
+.addValueToField({
+    name: 'state',
+    table: 'ies_ens_superior',
+    tableField: 'cod_uf_ies',
+    resultField: 'state_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_uf_ies',
+	    table: 'ies_ens_superior'
+    }
+}, 'filter')
+.addValueToField({
+    name: 'min_year',
+    table: 'curso_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo',
+        table: 'curso_ens_superior'
+    }
+}, 'filter')
+.addValueToField({
+    name: 'max_year',
+    table: 'curso_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo',
+        table: 'ies_ens_superior'
+    }
+}, 'filter')
+.addValue({
+    name: 'upper_adm_dependency',
+    table: 'curso_ens_superior',
+    tableField: 'par_categoria_administrativa',
+    resultField: 'upper_adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_categoria_administrativa', //cod_categoria_administrativa
+        table: 'curso_ens_superior'
+    }
+})
+.addValue({
+    name: 'course',
+    table: 'curso_ens_superior',
+    tableField: 'nome_curso',
+    resultField: 'course_name',
+    where: {
+        relation: '=',
+        type: 'string',
+        field: 'nome_curso',
+        table: 'curso_ens_superior'
+    }
+})
+.addValue({
+    name: 'upper_education_mod',
+    table: 'curso_ens_superior',
+    tableField: 'cod_modalidade_ensino',
+    resultField: 'upper_education_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_modalidade_ensino',
+        table: 'curso_ens_superior'
+    }
+})
+.addValue({
+    name: 'academic_organization',
+    table: 'curso_ens_superior',
+    tableField: 'cod_organizacao_academica',
+    resultField: 'academic_organization_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_organizacao_academica',
+        table: 'curso_ens_superior'
+    }
+})
+.addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio_ies',
+        foreignTable: 'ies_ens_superior'
+    }
+})
+.addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio_ies',
+        foreignTable: 'ies_ens_superior'
+    }
+})
+.addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['id', 'nome'],
+    resultField: ['city_id', 'city_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio_ies',
+        foreignTable: 'ies_ens_superior'
+    }
+})
+
+
+
+courseStudentsApp.get('/', rqf.parse(), (req, res, next) => {
+    req.sql.field("curso_ens_superior.ano_censo", "year")
+    .field("SUM(curso_ens_superior.quantidade_inscritos_total)", "inscritos_total")
+    .field("SUM(curso_ens_superior.quantidade_vagas_totais)", "vagas_totais")
+    .field("SUM(curso_ens_superior.quantidade_ingresso_curso)", "ingresso_curso")
+    .from("curso_ens_superior")
+    .join("ies_ens_superior ON curso_ens_superior.ano_censo = ies_ens_superior.ano_censo AND curso_ens_superior.cod_ies = ies_ens_superior.cod_ies")
+    .where("curso_ens_superior.cod_nivel_academico = 1")
+    .where("curso_ens_superior.cod_grau_academico = 2 OR curso_ens_superior.cod_grau_academico = 4")
+    .group("curso_ens_superior.ano_censo")
+    .order("curso_ens_superior.ano_censo")
+    next();
+
+}, rqf.build(), query, (req, res, next) => {
+    for (var res of req.result){
+        res.inscritos_total = Number(res.inscritos_total);
+        res.vagas_totais = Number(res.vagas_totais);
+        res.ingresso_curso = Number(res.ingresso_curso);
+        res.total = null;
+    }
+
+    next();
+}, id2str.transform(), response('course_students'))
+
+module.exports = courseStudentsApp;
diff --git a/src/libs/routes_v2/cub.js b/src/libs/routes_v2/cub.js
new file mode 100644
index 00000000..cfc2848a
--- /dev/null
+++ b/src/libs/routes_v2/cub.js
@@ -0,0 +1,227 @@
+const express = require('express');
+
+const cubApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const passport = require('passport');
+
+const download = require(`${libs}/middlewares/downloadDatabase`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+let rqfCount = new ReqQueryFields();
+
+cubApp.get('/year_range', (req, res, next) => {
+    req.sql.from('cub')
+    .field('MIN(cub.ano_censo)', 'start_year')
+    .field('MAX(cub.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+cubApp.get('/years', (req, res, next) => {
+    req.sql.from('cub')
+    .field('DISTINCT cub.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+cubApp.get('/months', (req, res, next) => {
+    req.sql.from('cub')
+    .field('DISTINCT cub.mes_censo', 'month');
+    next();
+}, query, response('months'));
+
+cubApp.get('/years_months', (req, res, next) => {
+    req.sql.from('cub')
+    .field('DISTINCT cub.ano_censo AS "year", cub.mes_censo AS "month"');
+    next();
+}, query, response('years_months'));
+
+cubApp.get('/price_type', (req, res, next) => {
+    req.sql.from('cub')
+    .field('DISTINCT cub.tipo_preco', 'price_type');
+    next();
+}, query, response('price_type'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['sigla', 'id'],
+    resultField: ['sigla_uf', 'cod_uf'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'cub'
+    },
+    // join: {
+    //     primary: 'id',
+    //     foreign: 'estado_id',
+    //     foreignTable: 'cub'
+    // }
+}).addValue({
+    name: 'min_year',
+    table: 'cub',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'cub',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'cub',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'cub',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'min_month',
+    table: 'cub',
+    tableField: 'mes_censo',
+    resultField: 'month',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'cub',
+        field: 'mes_censo'
+    }
+}).addValue({
+    name: 'max_month',
+    table: 'cub',
+    tableField: 'mes_censo',
+    resultField: 'month',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'cub',
+        field: 'mes_censo'
+    }
+});
+
+cubApp.get('/last_state_values', rqf.parse(), rqf.build(), (req, res, next) => {
+
+    var price_by_id = squel.select().from('cub')
+    .field('estado_id')
+    .field('MAX(ano_censo*100 + mes_censo)', 'ano_censo')
+    .group('estado_id')
+
+    if (req.filter.size || req.dims.size){
+        if ('state' in req.filter || 'state' in req.dims){
+            //req.sql = states
+            req.sql.from('cub')
+            .field('cub.ano_censo', 'ano')
+            .field('cub.mes_censo', 'mes')
+            .field('cub.tipo_preco', 'tipo_preco')
+            .field('cub.preco', 'preco')
+            .join(
+                price_by_id, 
+                'sub', 
+                'cub.estado_id = sub.estado_id AND cub.ano_censo = (sub.ano_censo/100)'
+            )
+            .join('estado', null, 'cub.estado_id = estado.id')
+            .group('cub.ano_censo')
+            .group('cub.mes_censo')
+            .group('cub.tipo_preco')
+            .group('cub.preco')
+        }
+        else{
+            req.sql.from("cub")
+        }
+    }
+    else{
+        //req.sql = average
+        req.sql.from(
+            squel.select().from('cub')
+            .field('cub.tipo_preco', 'tipo_preco')
+            .field('cub.preco', 'preco')
+            .join(price_by_id, 'sub', 
+                'cub.estado_id = sub.estado_id AND cub.ano_censo = (sub.ano_censo/100)'
+            )
+            .join('estado', null, 'cub.estado_id = estado.id')
+            , "states")
+        .field('AVG(states.preco)', 'preco')
+        .field("'BR'", 'sigla_uf')
+        .field('states.tipo_preco')
+        .group('states.tipo_preco')
+    }
+    next();
+}, query, id2str.transform(), response('last_state_values'))
+
+cubApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    if (req.filter.size || req.filter.dims) {
+        if ('state' in req.filter || 'state' in req.dims) {
+            req.sql.from('cub')
+            .field('cub.estado_id', 'cod_uf')
+            .field('estado.sigla', 'sigla_uf')
+            .field('cub.tipo_preco', 'tipo_preco')
+            .field('cub.preco', 'preco')
+            .join('estado', null, 'cub.estado_id=estado.id')
+            .group('cub.ano_censo')
+            .group('cub.mes_censo')
+            .group('cub.estado_id')
+            .group('estado.sigla')
+            .group('cub.tipo_preco')
+            .group('cub.preco')
+        } else {
+            req.sql.from('cub')
+            .field("'BR'", 'sigla_uf')
+            .field("cub.tipo_preco", 'tipo_preco')
+            .field('AVG(cub.preco)', 'preco')
+            .join('estado', null, 'cub.estado_id=estado.id')
+            .group('cub.ano_censo')
+            .group('cub.mes_censo')
+            .group('cub.tipo_preco')
+        }
+    } else {
+        req.sql.from('cub')
+        .field('cub.estado_id', 'cod_uf')
+        .field('estado.sigla', 'sigla_uf')
+        .field('cub.tipo_preco', 'tipo_preco')
+        .field('cub.preco', 'preco')
+        .join('estado', null, 'cub.estado_id=estado.id')
+        .group('cub.ano_censo')
+        .group('cub.mes_censo')
+        .group('cub.estado_id')
+        .group('estado.sigla')
+        .group('cub.tipo_preco')
+        .group('cub.preco')
+    }
+
+    next();
+}, query, id2str.transform(), response('cub'));
+
+module.exports = cubApp;
diff --git a/src/libs/routes_v2/dailyChargeAmount.js b/src/libs/routes_v2/dailyChargeAmount.js
new file mode 100644
index 00000000..4af6cb7d
--- /dev/null
+++ b/src/libs/routes_v2/dailyChargeAmount.js
@@ -0,0 +1,467 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const dailyChargeAmountApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: { include: [200] } }).middleware;
+
+let rqf = new ReqQueryFields();
+
+let rqfCount = new ReqQueryFields();
+
+//dailyChargeAmountApp.use(cache('15 day'));
+
+dailyChargeAmountApp.get('/year_range', (req, res, next) => {
+    req.sql.from('turma')
+        .field('MIN(turma.ano_censo)', 'start_year')
+        .field('MAX(turma.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+dailyChargeAmountApp.get('/years', (req, res, next) => {
+    req.sql.from('turma')
+        .field('DISTINCT turma.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+dailyChargeAmountApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+        .field('fonte', 'source')
+        .where('tabela = \'turma\'');
+    next();
+}, query, response('source'));
+
+dailyChargeAmountApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+dailyChargeAmountApp.get('/adm_dependency_detailed', cache('15 day'), (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+dailyChargeAmountApp.get('/location', cache('15 day'), (req, res, next) => {
+    req.result = [
+        { id: 1, name: 'Urbana' },
+        { id: 2, name: 'Rural' }
+    ];
+    next();
+}, response('location'));
+
+dailyChargeAmountApp.get('/rural_location', (req, res, next) => {
+    req.result = [
+        { id: 1, name: "Urbana" },
+        { id: 2, name: "Rural" },
+        { id: 3, name: "Rural - Área de assentamento" },
+        { id: 4, name: "Rural - Terra indígena" },
+        { id: 5, name: "Rural - Área remanescente de quilombos" },
+        { id: 6, name: "Rural - Unidade de uso sustentável" }
+    ];
+    next();
+}, response('rural_location'));
+
+dailyChargeAmountApp.get('/education_level_short', (req, res, next) => {
+    req.result = [
+        { id: null, name: 'Não classificada' },
+        { id: 1, name: 'Creche' },
+        { id: 2, name: 'Pré-Escola' },
+        { id: 3, name: 'Ensino Fundamental - anos iniciais' },
+        { id: 4, name: 'Ensino Fundamental - anos finais' },
+        { id: 5, name: 'Ensino Médio' },
+        { id: 6, name: 'EJA' },
+        { id: 7, name: 'EE exclusiva' }
+    ];
+    next();
+}, response('education_level_short'));
+
+dailyChargeAmountApp.get('/average/education_level_mod', (req, res, next) => {
+    req.result = [
+        { id: null, name: 'Não classificada' },
+        { id: 1, name: 'Creche' },
+        { id: 2, name: 'Pré-Escola' },
+        { id: 3, name: 'Educação Infantil Unificada' },
+        { id: 4, name: 'Ensino Fundamental - anos iniciais' },
+        { id: 5, name: 'Ensino Fundamental - anos finais' },
+        { id: 6, name: 'Ensino Médio' },
+        { id: 7, name: 'Turma Multisseriadas e Multietapas' },
+        { id: 8, name: 'EJA - Ensino Fundamental' },
+        { id: 9, name: 'EJA - Ensino Médio' },
+        { id: 10, name: 'Educação Profissional' }
+    ];
+    next();
+}, response('education_level_mod'));
+
+dailyChargeAmountApp.get('/period', (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 3; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.period(i)
+        });
+    };
+    req.result.push({
+        id: 99,
+        name: id2str.period(99)
+    });
+    next();
+}, response('period'));
+
+dailyChargeAmountApp.get('/integral_time', (req, res, next) => {
+    req.result = [
+        { id: null, name: 'Não Disponível' },
+        { id: 0, name: 'Não' },
+        { id: 1, name: 'Sim' }
+    ];
+    next();
+}, response('integral_time'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'turma'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'turma'
+    }
+}).addValue({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'turma'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'turma'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'turma'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'turma',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'turma',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'turma',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'turma',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'location',
+    table: 'turma',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'turma',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'turma',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'turma',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'education_level_mod',
+    table: 'turma',
+    tableField: 'etapas_mod_ensino_segmento_id',
+    resultField: 'education_level_mod_id',
+    dontGroup: true,
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapas_mod_ensino_segmento_id'
+    }
+}).addValue({
+    name: 'period',
+    table: 'turma',
+    tableField: 'turma_turno_id',
+    resultField: 'period_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'turma_turno_id'
+    }
+}).addValue({
+    name: 'integral_time',
+    table: 'turma',
+    tableField: 'tempo_integral',
+    resultField: 'integral_time_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'tempo_integral'
+    }
+}).addValue({
+    name: 'education_level_short',
+    table: 'turma',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+});
+
+dailyChargeAmountApp.get('/', rqf.parse(), (req, res, next) => {
+    var status = 0;
+    if ('period' in req.filter) {
+        if (req.filter['period'].length == 1 && (req.filter['period'][0] == '3' || req.filter['period'][0] === '4')) {
+            status = 1;
+        } else if (req.filter['period'].length <= 2 && (req.filter['period'].includes('1') || req.filter['period'].includes('2')) && (!req.filter['period'].includes('3'))) {
+            status = 1;
+        }
+    } else if (req.filter['integral_time'] == '1') {
+        status = 1;
+    }
+
+    if (status) {
+        req.sql.from('turma')
+            .field('turma.ano_censo', 'year')
+            .field('turma.etapa_resumida', 'education_level_short_id')
+            .field('AVG(turma.duracao_turma)/60.0', 'average_class_duration')
+            .field('MEDIAN(turma.duracao_turma)/60.0', 'median_class_duration')
+            .field('STDDEV_SAMP(turma.duracao_turma)/60.0', 'std_class_duration')
+            .field('QUANTILE(turma.duracao_turma, 0.25)/60.0', 'fstqt_class_duration')
+            .field('QUANTILE(turma.duracao_turma, 0.75)/60.0', 'thdqt_class_duration')
+            .group('turma.ano_censo')
+            .group('turma.etapa_resumida')
+            .order('turma.ano_censo')
+            .order('turma.etapa_resumida')
+            .where('((turma.tipo_turma_id <= 3 AND turma.tipo_atendimento_id is NULL) OR (turma.tipo_atendimento_id <= 2 AND turma.tipo_turma_id is NULL)) and turma.dependencia_adm_id <= 3')
+    } else {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Wrong/No filter specified'
+        });
+    }
+
+    next();
+
+}, rqf.build(), query, addMissing(rqf), (req, res, next) => {
+
+    function sliced(object) {
+        return object['education_level_short_id'] > 3;
+    }
+
+    if ('period' in req.filter || 'period' in req.dims) {
+        req.filter['period'].forEach((element) => {
+            if (element == '3')
+                req.result = req.result.filter(sliced);
+        });
+    }
+
+    next();
+}, id2str.transform(), response('turma'));
+
+dailyChargeAmountApp.get('/average', rqf.parse(), rqf.build(), (req, res, next) => {
+    var status = 0;
+    if (('education_level_mod' in req.filter || 'education_level_mod' in req.dims)
+        && ('integral_time' in req.filter)) {
+        if (req.filter['integral_time'] == '0'
+            && ('period' in req.filter)) {
+            if (req.filter['period'].length == 1
+                && req.filter['period'][0] == '3') {
+                status = 1;
+            } else if (req.filter['period'].length <= 2
+                && (req.filter['period'].includes('1')
+                    || req.filter['period'].includes('2'))
+                && (!req.filter['period'].includes('3'))) {
+                status = 1;
+            }
+        } else if (req.filter['integral_time'] == '1') {
+            status = 1;
+        }
+    }
+
+    if (status) {
+        let baseQ = req.sql.clone();
+
+        let tableR = baseQ.clone();
+        tableR.from('turma')
+            .field('duracao_turma')
+            .field('etapas_mod_ensino_segmento_id')
+            .field('ROW_NUMBER() OVER(PARTITION BY etapas_mod_ensino_segmento_id ORDER BY duracao_turma)', 'rowno')
+            .where('((turma.tipo_turma_id <= 3 AND turma.tipo_atendimento_id is NULL) OR (turma.tipo_atendimento_id <= 2 AND turma.tipo_turma_id is NULL))')
+
+        let tableG = baseQ.clone();
+        tableG.from('turma')
+            .field('1+COUNT(*)', 'counter')
+            .field('etapas_mod_ensino_segmento_id')
+            .where('((turma.tipo_turma_id <= 3 AND turma.tipo_atendimento_id is NULL) OR (turma.tipo_atendimento_id <= 2 AND turma.tipo_turma_id is NULL))')
+            .group('etapas_mod_ensino_segmento_id')
+
+        let joinRG = squel.select();
+        joinRG.from(tableR, 'R')
+            .field('R.etapas_mod_ensino_segmento_id')
+            .field('AVG(1.0*R.duracao_turma)/60', 'median_value')
+            .join(tableG, 'G', 'R.etapas_mod_ensino_segmento_id = G.etapas_mod_ensino_segmento_id AND R.rowNo BETWEEN G.counter/2 AND G.counter/2+G.counter%2')
+            .group('R.etapas_mod_ensino_segmento_id')
+
+        req.sql
+            .from('turma')
+            .from(joinRG, 'm')
+            .field('turma.ano_censo', 'year')
+            .field('turma.etapas_mod_ensino_segmento_id', 'education_level_mod_id')
+            .field('AVG(turma.duracao_turma)/60.0', 'average_class_duration')
+            .field('AVG(m.median_value)', 'median_class_duration')
+            .field('STDDEV_SAMP(turma.duracao_turma)/60.0', 'std_class_duration')
+            .field('QUANTILE(turma.duracao_turma, 0.25)/60.0', 'fstqt_class_duration')
+            .field('QUANTILE(turma.duracao_turma, 0.75)/60.0', 'thdqt_class_duration')
+            .group('turma.ano_censo')
+            .group('turma.etapas_mod_ensino_segmento_id')
+            .order('turma.ano_censo')
+            .order('turma.etapas_mod_ensino_segmento_id')
+            .where('((turma.tipo_turma_id <= 3 AND turma.tipo_atendimento_id is NULL) OR (turma.tipo_atendimento_id <= 2 AND turma.tipo_turma_id is NULL)) \
+                and m.etapas_mod_ensino_segmento_id = turma.etapas_mod_ensino_segmento_id')
+    } else {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Wrong/No filter specified'
+        });
+    }
+
+    next();
+}, query, addMissing(rqf), id2str.transform(), response('turma'));
+
+module.exports = dailyChargeAmountApp;
diff --git a/src/libs/routes_v2/disciplines.js b/src/libs/routes_v2/disciplines.js
new file mode 100644
index 00000000..63b05f32
--- /dev/null
+++ b/src/libs/routes_v2/disciplines.js
@@ -0,0 +1,682 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const disciplinesApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: { include: [200] } }).middleware;
+
+let rqf = new ReqQueryFields();
+
+disciplinesApp.use(cache('15 day'));
+
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+disciplinesApp.get('/year_range', (req, res, next) => {
+    req.sql.from('docente')
+        .field('MIN(docente.ano_censo)', 'start_year')
+        .field('MAX(docente.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+disciplinesApp.get('/years', (req, res, next) => {
+    req.sql.from('docente').
+        field('DISTINCT docente.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+disciplinesApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+        .field('fonte', 'source')
+        .where('tabela = \'docente\'');
+    next();
+}, query, response('source'));
+
+disciplinesApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 8; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+disciplinesApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+disciplinesApp.get('/education_level_mod', (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 12; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.educationLevelMod(i)
+        });
+    }
+    req.result.push({
+        id: 99,
+        name: id2str.educationLevelMod(99)
+    });
+    next();
+}, response('education_level_mod'));
+
+disciplinesApp.get('/education_level_short', (req, res, next) => {
+    req.result = [
+        { id: null, name: 'Não classificada' },
+        { id: 1, name: 'Creche' },
+        { id: 2, name: 'Pré-Escola' },
+        { id: 3, name: 'Ensino Fundamental - anos iniciais' },
+        { id: 4, name: 'Ensino Fundamental - anos finais' },
+        { id: 5, name: 'Ensino Médio' },
+        { id: 6, name: 'EJA' },
+        { id: 7, name: 'EE exclusiva' }
+    ];
+    next();
+}, response('education_level_short'));
+
+disciplinesApp.get('/location', (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 2; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.location(i)
+        });
+    };
+    next();
+}, response('location'));
+
+disciplinesApp.get('/rural_location', (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 8; i++) {
+        req.result.push({
+            id: i,
+            name: id2str.ruralLocation(i)
+        });
+    };
+    next();
+}, response('rural_location'));
+
+disciplinesApp.get('/education_type', (req, res, next) => {
+    req.sql.from('docente')
+        .field('DISTINCT nivel_tipo_formacao', 'id')
+        .order('id');
+    next();
+}, query, (req, res, next) => {
+    req.result.forEach((result) => {
+        result.name = id2str.educationType(result.id);
+    });
+    next();
+}, response('education_type'));
+
+disciplinesApp.get('/gender', (req, res, next) => {
+    req.result = [
+        { id: 1, name: 'Masculino' },
+        { id: 2, name: 'Feminino' }
+    ];
+    next();
+}, response('gender'));
+
+
+disciplinesApp.get('/contract_type', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.contractType("null")
+    }];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.contractType(i)
+        });
+    }
+    next();
+}, response('contract_type'));
+
+disciplinesApp.get('/ethnic_group', (req, res, next) => {
+    req.result = [];
+    for (let i = 0; i <= 5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ethnicGroup(i)
+        });
+    }
+    next();
+}, response('ethnic_group'));
+
+disciplinesApp.get('/discipline', (req, res, next) => {
+    req.result = [];
+    for (let i = 1; i <= 19; i++) {
+        req.result.push({
+            id: i,
+            name: id2str.discipline(i)
+        })
+    }
+    next();
+}, response('discipline'))
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'adm_dependency',
+    table: 'docente',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'docente',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'contract_type',
+    table: 'docente',
+    tableField: 'tipo_contratacao',
+    resultField: 'contract_type_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'tipo_contratacao'
+    }
+}).addValue({
+    name: 'education_level_mod',
+    table: 'docente',
+    tableField: 'etapas_mod_ensino_segmento_id',
+    resultField: 'education_level_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapas_mod_ensino_segmento_id'
+    }
+}).addValue({
+    name: 'education_level_short',
+    table: 'docente',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+}).addValue({
+    name: 'education_type',
+    table: 'docente',
+    tableField: 'nivel_tipo_formacao',
+    resultField: 'education_type_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'nivel_tipo_formacao'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_regiao_id',
+        foreignTable: 'docente'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_estado_id',
+        foreignTable: 'docente'
+    }
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}, 'filter').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'docente'
+    }
+}, 'dims').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'docente'
+    }
+}, 'filter').addValue({
+    name: 'location',
+    table: 'docente',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'docente',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'docente',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'docente',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'gender',
+    table: 'docente',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'ethnic_group',
+    table: 'docente',
+    tableField: 'cor_raca',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca'
+    }
+}).addValue({
+    name: 'discipline',
+    table: 'docente',
+    tableField: '',
+    resultField: '',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: ''
+    }
+});
+
+disciplinesApp.get('/', rqf.parse(), (req, res, next) => {
+    if ('discipline' in req.dims) {
+        // delete req.filter.discipline;
+        delete req.dims.discipline;
+        req.tmp_discipline = true;
+
+        req.sql.field('SUM(n_disc)', 'total')
+            .field('SUM(n_disc_adequada)', 'total_suitable')
+
+            .field('SUM(quimica_not_null)', 'total_quimica')
+            .field('SUM(adequacao_quimica)', 'total_suitable_quimica')
+
+            .field('SUM(fisica_not_null)', 'total_fisica')
+            .field('SUM(adequacao_fisica)', 'total_suitable_fisica')
+
+            .field('SUM(matematica_not_null)', 'total_matematica')
+            .field('SUM(adequacao_matematica)', 'total_suitable_matematica')
+
+            .field('SUM(biologia_not_null)', 'total_biologia')
+            .field('SUM(adequacao_biologia)', 'total_suitable_biologia')
+
+            .field('SUM(ciencias_not_null)', 'total_ciencias')
+            .field('SUM(adequacao_ciencias)', 'total_suitable_ciencias')
+
+            .field('SUM(lingua_portuguesa_not_null)', 'total_lingua_portuguesa')
+            .field('SUM(adequacao_lingua_portuguesa)', 'total_suitable_lingua_portuguesa')
+
+            .field('SUM(lingua_inglesa_not_null)', 'total_lingua_inglesa')
+            .field('SUM(adequacao_lingua_inglesa)', 'total_suitable_lingua_inglesa')
+
+            .field('SUM(lingua_espanhola_not_null)', 'total_lingua_espanhola')
+            .field('SUM(adequacao_lingua_espanhola)', 'total_suitable_lingua_espanhola')
+
+            .field('SUM(lingua_francesa_not_null)', 'total_lingua_francesa')
+            .field('SUM(adequacao_lingua_francesa)', 'total_suitable_lingua_francesa')
+
+            .field('SUM(lingua_outra_not_null)', 'total_lingua_outra')
+            .field('SUM(adequacao_lingua_outra)', 'total_suitable_lingua_outra')
+
+            .field('SUM(lingua_indigena_not_null)', 'total_lingua_indigena')
+            .field('SUM(adequacao_lingua_indigena)', 'total_suitable_lingua_indigena')
+
+            .field('SUM(artes_not_null)', 'total_artes')
+            .field('SUM(adequacao_artes)', 'total_suitable_artes')
+
+            .field('SUM(educacao_fisica_not_null)', 'total_educacao_fisica')
+            .field('SUM(adequacao_educacao_fisica)', 'total_suitable_educacao_fisica')
+
+            .field('SUM(historia_not_null)', 'total_historia')
+            .field('SUM(adequacao_historia)', 'total_suitable_historia')
+
+            .field('SUM(geografia_not_null)', 'total_geografia')
+            .field('SUM(adequacao_geografia)', 'total_suitable_geografia')
+
+            .field('SUM(filosofia_not_null)', 'total_filosofia')
+            .field('SUM(adequacao_filosofia)', 'total_suitable_filosofia')
+
+            .field('SUM(ensino_religioso_not_null)', 'total_ensino_religioso')
+            .field('SUM(adequacao_ensino_religioso)', 'total_suitable_ensino_religioso')
+
+            .field('SUM(estudos_sociais_not_null)', 'total_estudos_sociais')
+            .field('SUM(adequacao_estudos_sociais)', 'total_suitable_estudos_sociais')
+
+            .field('SUM(sociologia_not_null)', 'total_sociologia')
+            .field('SUM(adequacao_sociologia)', 'total_suitable_sociologia')
+
+            .field("'Brasil'", 'name')
+            .field('docente.ano_censo', 'year')
+            .from('docente')
+            .group('docente.ano_censo')
+            .order('docente.ano_censo')
+            .where('(docente.tipo_docente = 1 OR docente.tipo_docente = 5) AND \
+            ((docente.tipo_turma_id >= 0 AND docente.tipo_turma_id <= 3 AND docente.tipo_turma_atendimento_id is NULL) \
+            OR ((docente.tipo_turma_atendimento_id = 1 OR docente.tipo_turma_atendimento_id = 2) AND docente.tipo_turma_id is NULL)) AND \
+            docente.etapas_mod_ensino_segmento_id <> 6 AND docente.etapas_mod_ensino_segmento_id <> 12');
+    }
+    else if ('discipline' in req.filter) {
+        const disciplines = ['quimica', 'fisica', 'matematica', 'biologia', 'ciencias', 'lingua_portuguesa', 'lingua_inglesa', 'lingua_espanhola', 'lingua_francesa', 'lingua_outra', 'lingua_indigena', 'artes', 'educacao_fisica', 'historia', 'geografia', 'filosofia', 'ensino_religioso', 'estudos_sociais', 'sociologia']
+
+        let totalQuery = 'SUM('
+        let totalSuitableQuery = 'SUM('
+
+        if (!(req.filter.discipline instanceof Array)) req.filter.discipline = [req.filter.discipline]
+
+        req.filter.discipline.forEach(d => {
+            totalQuery += disciplines[d - 1] + '_not_null + '
+            totalSuitableQuery += 'adequacao_' + disciplines[d - 1] + ' + '
+        })
+        totalQuery = totalQuery.slice(0, -2) + ')'
+        totalSuitableQuery = totalSuitableQuery.slice(0, -2) + ')'
+
+        delete req.filter.discipline;
+
+        req.sql.field(totalQuery, 'total')
+            .field(totalSuitableQuery, 'total_suitable')
+            .field("'Brasil'", 'name')
+            .field('docente.ano_censo', 'year')
+            .from('docente')
+            //    .join('turma', null, 'docente.turma_id=turma.id AND docente.ano_censo=turma.ano_censo')
+            .group('docente.ano_censo')
+            .order('docente.ano_censo')
+            .where('(docente.tipo_docente = 1 OR docente.tipo_docente = 5) AND \
+                ((docente.tipo_turma_id >= 0 AND docente.tipo_turma_id <= 3 AND docente.tipo_turma_atendimento_id is NULL) \
+                OR ((docente.tipo_turma_atendimento_id = 1 OR docente.tipo_turma_atendimento_id = 2) AND docente.tipo_turma_id is NULL)) AND \
+                docente.etapas_mod_ensino_segmento_id <> 6 AND docente.etapas_mod_ensino_segmento_id <> 12');
+    }
+    else {
+        req.sql.field('SUM(n_disc)', 'total')
+            .field('SUM(n_disc_adequada)', 'total_suitable')
+            .field("'Brasil'", 'name')
+            .field('docente.ano_censo', 'year')
+            .from('docente')
+            //    .join('turma', null, 'docente.turma_id=turma.id AND docente.ano_censo=turma.ano_censo')
+            .group('docente.ano_censo')
+            .order('docente.ano_censo')
+            .where('(docente.tipo_docente = 1 OR docente.tipo_docente = 5) AND \
+            ((docente.tipo_turma_id >= 0 AND docente.tipo_turma_id <= 3 AND docente.tipo_turma_atendimento_id is NULL) \
+            OR ((docente.tipo_turma_atendimento_id = 1 OR docente.tipo_turma_atendimento_id = 2) AND docente.tipo_turma_id is NULL)) AND \
+            docente.etapas_mod_ensino_segmento_id <> 6 AND docente.etapas_mod_ensino_segmento_id <> 12');
+    }
+
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    let filters = Object.keys(req.filter)
+	
+	// if(filters.includes("state")) {
+	// 	const disciplinesDB = ['quimica', 'fisica', 'matematica', 'biologia', 'ciencias', 'lingua_portuguesa', 'lingua_inglesa', 'lingua_espanhola',
+	// 		'lingua_francesa', 'lingua_outra', 'lingua_indigena', 'artes', 'educacao_fisica', 'historia', 'geografia', 'filosofia', 'ensino_religioso',
+	// 		'estudos_sociais', 'sociologia']
+	// 	const disciplinesAPI = ["Química", "Física", "Matemática", "Biologia", "Ciências", "Língua Portuguesa", "Língua Estrangeira – Inglês",
+	// 		"Língua Estrangeira - Espanhol","Língua Estrangeira - Francês", "Língua Estrangeira - Outras", "Língua Indígena", "Arte", "Educação Física", "História",
+	// 		"Geografia", "Filosofia", "Ensino religioso", "Estudos Sociais", "Sociologia"]
+	
+	// 	let jsonKeys = []
+	// 	let results = []
+	// 	req.result.forEach((r) => {
+	// 		jsonKeys = Object.keys(r)
+		
+	// 		let i
+	// 		let size = jsonKeys.length - 2  // Last two infos are "name" and "year"
+	// 		for(i = 0; i < size; i++) {
+	// 			let total_name = jsonKeys[i]
+	// 			let suitable_name = jsonKeys[i + 1]
+			
+	// 			// Calculates percentage
+	// 			let percentage = r[suitable_name] / r[total_name]
+	// 			percentage = percentage * 100
+	// 			percentage = percentage.toFixed(1)	// Rounds to 1 digit after comma, returns string
+	// 			percentage = percentage.replace(".", ",") + "%"
+
+	// 			// Parses name
+	// 	        total_name = total_name.replace("total_", "")
+	// 			let discipline_index = disciplinesDB.indexOf(total_name)
+	// 			let discipline_name = disciplinesAPI[discipline_index]
+
+	// 			let obj = {
+	// 				total: percentage,
+	// 				name: r["name"],
+	// 				year: r["year"],
+	// 				discipline_id: discipline_index + 1,	// Convert function starts at 1, not at 0
+	// 				discipline_name: discipline_name
+	// 			}
+	// 			results.push(obj)
+			
+	// 			i++;    // Ignore next, it's a suitable already used
+	// 		}
+	// 	})
+
+	// 	req.result = results;
+	// }
+	// else {
+		let disciplinesNotSuitable = [];
+		let disciplinesSuitable = [];
+
+		req.result.forEach((r) => {
+
+            let obj = {
+                sum_total: 0,
+                sum_suitable: 0
+            }
+
+	        Object.keys(r).forEach(k => {
+                if (k !== 'total' && k !== 'total_suitable') 
+                    obj[k] = r[k];
+			})
+
+            if (req.tmp_discipline){
+                Object.keys(r).forEach(k => {
+                    if (/^total_suitable_/.test(k)) // if k starts with total_suitable
+                        obj.sum_suitable += parseInt(r[k]);
+                    else if (/^total_(?!suitable)/.test(k))
+                        obj.sum_total += parseInt(r[k]);
+                })
+            } else {
+                delete obj.sum_total;
+                delete obj.sum_suitable;
+            }
+
+            let objNotSuitable = Object.assign({}, {
+				total: parseInt(r.total) - parseInt(r.total_suitable),
+				suitable: 0,
+                discipline_name: 'Formação não adequada',
+			}, obj)
+
+	        let objSuitable = Object.assign({}, {
+		        total: parseInt(r.total_suitable),
+			    suitable: 1,
+                discipline_name: 'Formação adequada',
+            }, obj)
+
+		    disciplinesNotSuitable.push(objNotSuitable)
+			disciplinesSuitable.push(objSuitable)
+		 })
+
+		 req.result = disciplinesNotSuitable.concat(disciplinesSuitable);
+    next();
+}, response('disciplines'));
+
+module.exports = disciplinesApp;
+
+
diff --git a/src/libs/routes_v2/distributionFactor.js b/src/libs/routes_v2/distributionFactor.js
new file mode 100644
index 00000000..8a7b0e28
--- /dev/null
+++ b/src/libs/routes_v2/distributionFactor.js
@@ -0,0 +1,210 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const distributionApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const multiQuery = require(`${libs}/middlewares/multiQuery`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+distributionApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'fatores_matricula'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'fatores_matricula'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'fatores_matricula'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'fatores_matricula'
+    }
+}, 'filter').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'fatores_matricula'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'fatores_matricula'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'fatores_matricula'
+    }
+})
+
+// Return all cities
+distributionApp.get('/', rqf.parse(), (req, res, next) => {
+    req.querySet = [];
+    req.queryIndex = {};
+
+    let relation = req.sql.clone();
+    relation.from('relacao_fatores_matricula').field('*');
+    req.queryIndex.relation = req.querySet.push(relation) - 1;
+
+    req.sql.from('fatores_matricula')
+    .field('fatores_matricula.municipio_id', 'municipio_id')
+    .field('fatores_matricula."mais_CRE_0"')
+	.field('fatores_matricula."mais_CRE_1"')
+	.field('fatores_matricula."mais_CRE_2"')
+	.field('fatores_matricula."mais_CRE_3"')
+	.field('fatores_matricula."mais_PRE"')
+	.field('fatores_matricula."mais_EFAI"')
+	.field('fatores_matricula."mais_EFAF"')
+	.field('fatores_matricula."mais_EM"')
+	.field('fatores_matricula."mais_EJA"')
+	.field('fatores_matricula."menos_CRE_0"')
+	.field('fatores_matricula."menos_CRE_1"')
+	.field('fatores_matricula."menos_CRE_2"')
+	.field('fatores_matricula."menos_CRE_3"')
+	.field('fatores_matricula."menos_PRE"')
+	.field('fatores_matricula."menos_EFAI"')
+	.field('fatores_matricula."menos_EFAF"')
+	.field('fatores_matricula."menos_EM"')
+	.field('fatores_matricula."menos_EJA"')
+    .group('fatores_matricula.municipio_id')
+    .group('fatores_matricula."mais_CRE_0"')
+	.group('fatores_matricula."mais_CRE_1"')
+	.group('fatores_matricula."mais_CRE_2"')
+	.group('fatores_matricula."mais_CRE_3"')
+	.group('fatores_matricula."mais_PRE"')
+	.group('fatores_matricula."mais_EFAI"')
+	.group('fatores_matricula."mais_EFAF"')
+	.group('fatores_matricula."mais_EM"')
+	.group('fatores_matricula."mais_EJA"')
+	.group('fatores_matricula."menos_CRE_0"')
+	.group('fatores_matricula."menos_CRE_1"')
+	.group('fatores_matricula."menos_CRE_2"')
+	.group('fatores_matricula."menos_CRE_3"')
+	.group('fatores_matricula."menos_PRE"')
+	.group('fatores_matricula."menos_EFAI"')
+	.group('fatores_matricula."menos_EFAF"')
+	.group('fatores_matricula."menos_EM"')
+	.group('fatores_matricula."menos_EJA"');
+
+    if(typeof req.dims.state !== 'undefined' || typeof req.filter.state !== 'undefined') {
+        req.sql.where('fatores_matricula.nivel = \'UF\'');
+    } else {
+        req.sql.where('fatores_matricula.nivel = \'BR\'');
+    }
+
+    next();
+}, rqf.build(), query, (req, res, next) => {
+    req.enrollmentFactor = req.result;
+    next();
+}, multiQuery, (req, res, next) => {
+    let relation = req.result[req.queryIndex.relation];
+    let result = [];
+    let first = true;
+    req.enrollmentFactor.forEach((city) => {
+        // if(first) console.log(city);
+        let obj = {
+            level: city.nivel,
+            region_id: city.regiao_id,
+            region_name: city.region_name,
+            state_id: city.state_id,
+            state_name: city.state_name,
+            city_id: city.municipio_id,
+            city_name: city.city_name,
+            series: []
+        };
+        // if(first) console.log(obj);
+        first = false;
+        relation.forEach((serie) => {
+            obj.series.push({
+                serie_id: serie.id,
+                distribution_factor_addition: city[serie.fator_adicao],
+                distribution_factor_reduction: city[serie.fator_reducao]
+            });
+        });
+        result.push(obj);
+    });
+    req.result = result;
+    next();
+}, response('ditributionFactor'));
+
+module.exports = distributionApp;
diff --git a/src/libs/routes_v2/downloads.js b/src/libs/routes_v2/downloads.js
new file mode 100644
index 00000000..2ec83b9e
--- /dev/null
+++ b/src/libs/routes_v2/downloads.js
@@ -0,0 +1,54 @@
+const express = require('express');
+
+const downloadApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const Download = require(`${libs}/models/download`);
+
+const User = require(`${libs}/models/user`);
+
+const passport = require('passport');
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+downloadApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, next) => {
+    request.get(config.cdn.url + '/api/v1/file', (err, response, body) => {
+        let cdn = JSON.parse(body);
+        Download.find({userId: req.user._id}, (err, downloads) => {
+            if (err) {
+                log.error(err);
+                return next(err);
+            }
+            
+            if(!downloads) {
+                res.statusCode = 404;
+                return res.json({msg: 'Nenhum download encontrado'});
+            } else {
+                downloads.forEach((dl) => {
+                    for(let i = 0; i < cdn.length; ++i) {
+                        if(cdn[i].query == dl.query) {
+                            dl.status = cdn[i].expired ? 'Expirado' : 'Enviado';
+                            dl.size = cdn[i].size;
+                            dl.expired = cdn[i].expired;
+                            dl.updatedAt = cdn[i].lastAccess;
+                            dl.link = config.cdn.download + '/' + cdn[i]._id;
+
+                            dl.save((err) => {
+                                if(err) log.error(err);
+                            });
+                            return;
+                        }
+                    }
+                });
+            }
+            res.json(downloads);
+        });
+    });
+});
+
+module.exports = downloadApp;
diff --git a/src/libs/routes_v2/educationYears.js b/src/libs/routes_v2/educationYears.js
new file mode 100644
index 00000000..0d103883
--- /dev/null
+++ b/src/libs/routes_v2/educationYears.js
@@ -0,0 +1,46 @@
+const express = require('express');
+
+const educationYearsApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const response = require(`${libs}/middlewares/response`);
+
+const config = require(`${libs}/config`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+educationYearsApp.use(cache('15 day'));
+
+educationYearsApp.get('/', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 7; ++i) {
+        let edLvlShort = {
+            id: i,
+            name: id2str.educationLevelShort(i),
+            schoolYears: []
+        };
+
+        for(let j = i*10; j <= (i*10 + 9); ++j) {
+
+            let schoolYear = {
+                id: j,
+                name: id2str.schoolYear(j)
+            };
+
+            if(schoolYear.name !== id2str.schoolYear(99)) {
+                edLvlShort.schoolYears.push(schoolYear);
+            }
+        }
+        if(edLvlShort.name !== id2str.schoolYear(99)) {
+            req.result.push(edLvlShort);
+        }
+    }
+    next();
+}, response('educationYears'));
+
+module.exports = educationYearsApp;
diff --git a/src/libs/routes_v2/educationalBudget.js b/src/libs/routes_v2/educationalBudget.js
new file mode 100644
index 00000000..30f6afc1
--- /dev/null
+++ b/src/libs/routes_v2/educationalBudget.js
@@ -0,0 +1,325 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+
+const libs = `${process.cwd()}/libs`;
+
+const conn = require(`${libs}/db/monet`);
+
+const express = require('express');
+
+const educationalBudget = express.Router();
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+let rqfId = new ReqQueryFields();
+
+const db = require(`${libs}/db/query_exec`);
+
+const log = require(`${libs}/log`)(module);
+
+rqfId.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'federative_entity',
+    table: 'orcamento_educacional',
+    tableField: 'entidade_federativa',
+    resultField: 'entidade_federativa_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'entidade_federativa'
+    }
+});
+
+//Insert route in orcamento_educacional table
+educationalBudget.post('/insert', (req, res, next) => {
+    let id = req.body.id || null;
+    let entidade_federativa = req.body.entidade_federativa || null;
+    let csv = req.body.csv || null;
+
+    //csv example
+    //"RO|7777777|jiarana|8043|41768845|43141895|66081767|8216|24312922|158,2%"
+
+     req.id = id;
+     req.entidade_federativa = entidade_federativa;
+     req.csv = csv.toString();
+     req.date = new Date();
+
+     //split o csv para adicionar separadamente nas colunas
+    csv = csv.split("|");
+    req.uf = csv[0];
+    req.municipio_id = csv[1];
+    req.nome = csv[2];
+    req.matriculas = csv[3];
+    req.receitas_vinculadas = csv[4];
+    req.despesas_realizadas = csv[5];
+    req.despesas_correntes = csv[6];
+    req.valor_aluno = csv[7];
+    req.completacao = csv[8];
+    req.completacao_porcentagem = csv[9];
+
+    let sqlQueryParams = [];
+
+    //remove id duplicate
+    let deleteQuery = squel.delete()
+        .from("orcamento_educacional")
+        .where('id = ' + req.id)
+        .toString()
+
+    //Exec delete sql
+    log.debug(`Executing SQL query '${deleteQuery}' with params '${[sqlQueryParams]}'`);
+    return new Promise((resolve, reject) => {
+        // Prepare statement
+        conn.prepare(deleteQuery, true).then((dbQuery) => {
+            // Execute query
+            dbQuery.exec(sqlQueryParams).then((dbResult) => {
+                // release resources allocated for the prepared statement
+                dbQuery.release();
+                resolve(dbResult.data);
+                req.result = "Insertion Success"
+                next();
+            }).catch((queryError) => {
+                log.error(`SQL query execution error: ${queryError.message}`);
+                req.result = "SQL query execution error:" + queryError.message;
+                log.error(`SQL query: ${deleteQuery} with params: ${sqlQueryParams}`);
+                // release resources allocated for the prepared statement
+                dbQuery.release();
+                next();
+            });
+        }).catch((prepError) => {
+            log.error(`SQL prepared statement error: ${prepError.message}`);
+            req.result = "SQL query execution error:" + queryError.message;
+            log.error(`SQL query: ${deleteQuery} with params: ${sqlQueryParams}`);
+            next();
+        });
+    });
+
+}, (req, res, next) => {
+    //build query
+    let insertQuery = squel.insert()
+        .into("orcamento_educacional")
+        .set("id", req.id)
+        .set("entidade_federativa", req.entidade_federativa)
+        .set("csv", req.csv)
+        .set("data", req.date.toString())
+        .set("uf", req.uf)
+        .set("municipio_id", req.municipio_id)
+        .set("nome", req.nome)
+        .set("receitas_vinculadas", req.receitas_vinculadas)
+        .set("despesas_realizadas", req.despesas_realizadas)
+        .set("despesas_correntes", req.despesas_correntes)
+        .set("valor_aluno", req.valor_aluno)
+        .set("completacao", req.completacao)
+        .set("completacao_porcentagem", req.completacao_porcentagem)
+        .toString()
+
+    let sqlQueryParams = [];
+
+    //Exec insert sql
+    log.debug(`Executing SQL query '${insertQuery}' with params '${[sqlQueryParams]}'`);
+    return new Promise((resolve, reject) => {
+        // Prepare statement
+        conn.prepare(insertQuery, true).then((dbQuery) => {
+            // Execute query
+            dbQuery.exec(sqlQueryParams).then((dbResult) => {
+                // release resources allocated for the prepared statement
+                dbQuery.release();
+                resolve(dbResult.data);
+                req.result = "Insertion Success"
+                next();
+            }).catch((queryError) => {
+                log.error(`SQL query execution error: ${queryError.message}`);
+                req.result = "SQL query execution error:" + queryError.message;
+                log.error(`SQL query: ${insertQuery} with params: ${sqlQueryParams}`);
+                // release resources allocated for the prepared statement
+                dbQuery.release();
+                next();
+            });
+        }).catch((prepError) => {
+            log.error(`SQL prepared statement error: ${prepError.message}`);
+            req.result = "SQL query execution error:" + queryError.message;
+            log.error(`SQL query: ${insertQuery} with params: ${sqlQueryParams}`);
+            next();
+        });
+    });
+
+    next();
+}, response('educationalBudget'));
+
+//Delete orcamento_educacional table
+educationalBudget.get('/delete', (req, res, next) => {
+
+    //build query
+    let insertQuery = squel.delete()
+        .from("orcamento_educacional")
+        .toString()
+
+    let sqlQueryParams = [];
+
+    //Exec sql in monet
+    log.debug(`Executing SQL query '${insertQuery}' with params '${[sqlQueryParams]}'`);
+    return new Promise((resolve, reject) => {
+        // Prepare statement
+        conn.prepare(insertQuery, true).then((dbQuery) => {
+            // Execute query
+            dbQuery.exec(sqlQueryParams).then((dbResult) => {
+                // release resources allocated for the prepared statement
+                dbQuery.release();
+                resolve(dbResult.data);
+                req.result = "Delete Table Success"
+                next();
+            }).catch((queryError) => {
+                log.error(`SQL query execution error: ${queryError.message}`);
+                req.result = "SQL query execution error:" + queryError.message;
+                log.error(`SQL query: ${insertQuery} with params: ${sqlQueryParams}`);
+                // release resources allocated for the prepared statement
+                dbQuery.release();
+                next();
+            });
+        }).catch((prepError) => {
+            log.error(`SQL prepared statement error: ${prepError.message}`);
+            req.result = "SQL query execution error:" + queryError.message;
+            log.error(`SQL query: ${insertQuery} with params: ${sqlQueryParams}`);
+            next();
+        });
+    });
+
+    next();
+}, response('educationalBudget'));
+
+//Return all id's in table
+educationalBudget.get('/id', rqfId.parse(), rqfId.build(), (req, res, next) => {
+        req.sql.from('orcamento_educacional')
+        .field('orcamento_educacional.municipio_id')
+        next();
+}, query, response('educationalBudget'));
+
+//Return count id grouop by entidade_federativa
+educationalBudget.get('/finish', rqf.parse(), (req, res, next) => {
+        req.sql.field('COUNT(*)', 'total')
+        .field('orcamento_educacional.entidade_federativa', 'entidade_federativa')
+        .from('orcamento_educacional')
+        .group('orcamento_educacional.entidade_federativa')
+        .order('orcamento_educacional.entidade_federativa')
+        next();
+}, query, response('educationalBudget'));
+
+//return all data
+educationalBudget.get('/', rqf.parse(), (req, res, next) => {
+        req.sql.from('orcamento_educacional')
+        .field('orcamento_educacional.id')
+        .field('orcamento_educacional.entidade_federativa', 'entidade_federativa')
+        .field('orcamento_educacional.data', 'data_insercao')
+        .field('orcamento_educacional.uf', 'uf')
+        .field('orcamento_educacional.municipio_id', 'municipio_id')
+        .field('orcamento_educacional.nome', 'nome')
+        .field('orcamento_educacional.receitas_vinculadas', 'receitas_vinculadas')
+        .field('orcamento_educacional.despesas_realizadas', 'despesas_realizadas')
+        .field('orcamento_educacional.despesas_correntes', 'despesas_correntes')
+        .field('orcamento_educacional.valor_aluno', 'valor_aluno')
+        .field('orcamento_educacional.completacao', 'completacao')
+        .field('orcamento_educacional.completacao_porcentagem', 'completacao_porcentagem')
+        .field('orcamento_educacional.csv', 'csv')
+        next();
+}, query, response('educationalBudget'));
+
+//Insert route in orcamento_educacional table
+educationalBudget.post('/insert_pqr', (req, res, next) => {
+    let id = JSON.parse(req.body.id) || null;
+    let pqr = JSON.parse(req.body.pqr) || null;
+
+     req.id = id;
+     req.pqr = pqr;
+     req.date = new Date();
+
+    //build query
+    let insertQuery = squel.insert()
+        .into("orcamento_educacional_pqr")
+        .set("id", req.id.toString())
+        .set("pqr", req.pqr.toString())
+        .set("data", req.date.toString())
+        .toString()
+
+    let sqlQueryParams = [];
+
+    //Exec sql
+    log.debug(`Executing SQL query '${insertQuery}' with params '${[sqlQueryParams]}'`);
+    return new Promise((resolve, reject) => {
+        // Prepare statement
+        conn.prepare(insertQuery, true).then((dbQuery) => {
+            // Execute query
+            dbQuery.exec(sqlQueryParams).then((dbResult) => {
+                // release resources allocated for the prepared statement
+                dbQuery.release();
+                resolve(dbResult.data);
+                req.result = "Insertion Success"
+                next();
+            }).catch((queryError) => {
+                log.error(`SQL query execution error: ${queryError.message}`);
+                req.result = "SQL query execution error:" + queryError.message;
+                log.error(`SQL query: ${insertQuery} with params: ${sqlQueryParams}`);
+                // release resources allocated for the prepared statement
+                dbQuery.release();
+                next();
+            });
+        }).catch((prepError) => {
+            log.error(`SQL prepared statement error: ${prepError.message}`);
+            req.result = "SQL query execution error:" + queryError.message;
+            log.error(`SQL query: ${insertQuery} with params: ${sqlQueryParams}`);
+            next();
+        });
+    });
+
+    next();
+}, response('educationalBudget'));
+
+educationalBudget.get('/get_pqr', (req, res, next) => {
+    req.sql.from('orcamento_educacional_pqr')
+    .field('orcamento_educacional_pqr.id', 'id')
+    .field('orcamento_educacional_pqr.pqr', 'pqr')
+    .field('orcamento_educacional_pqr.data', 'data')
+    next();
+}, query, response('educationalBudget'));
+
+module.exports = educationalBudget;
+
diff --git a/src/libs/routes_v2/employees.js b/src/libs/routes_v2/employees.js
new file mode 100644
index 00000000..1ef4e7fd
--- /dev/null
+++ b/src/libs/routes_v2/employees.js
@@ -0,0 +1,656 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const employeesApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]}  }).middleware;
+
+let rqfTeacher = new ReqQueryFields();
+
+let rqfSchool = new ReqQueryFields();
+
+
+employeesApp.use(cache('15 day'));
+
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+employeesApp.get('/year_range', (req, res, next) => {
+    req.sql.from('escola')
+    .field('MIN(escola.ano_censo)', 'start_year')
+    .field('MAX(escola.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+employeesApp.get('/years', (req, res, next) => {
+    req.sql.from('escola').
+    field('DISTINCT escola.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+employeesApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'docente\'');
+    next();
+}, query, response('source'));
+
+employeesApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+employeesApp.get('/diff_location', (req, res, next) => {
+    req.result = [
+        {id: 0, name: "A escola não está em localidade diferenciada"},
+        {id: 1, name: "Área de assentamento"},
+        {id: 2, name: "Terra indígena"},
+        {id: 3, name: "Terra remanescente de quilombos"},
+    ];
+    next();
+}, response('diff_location'));
+
+employeesApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+employeesApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+employeesApp.get('/function', (req, res, next) => {
+    req.result = [
+        {id: 0, name: "Administrativos"},
+        {id: 1, name: "Serviços Gerais"},
+        {id: 2, name: "Bibliotecário"},
+        {id: 3, name: "Saúde"},
+        {id: 4, name: "Coordenador"},
+        {id: 5, name: "Fonoaudiólogo"},
+        {id: 6, name: "Nutricionista"},
+        {id: 7, name: "Psicólogo"},
+        {id: 8, name: "Alimentação"},
+        {id: 9, name: "Pedagogia"},
+        {id: 10, name: "Secretário"},
+        {id: 11, name: "Segurança"},
+        {id: 12, name: "Monitores"},
+        {id: 99, name: "Não Classificado"}
+    ];
+    next();
+}, response('function'));
+
+rqfSchool.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'adm_dependency',
+    table: '@',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: '@',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}, 'filter').addValueToField({
+    name: 'school',
+    table: '@',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+}, 'dims').addValueToField({
+    name: 'school',
+    table: '@',
+    tableField: 'escola_nome',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+}, 'filter').addValue({
+    name: 'location',
+    table: '@',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'diff_location',
+    table: '@',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: '@',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'function',
+    table: '@',
+    tableField: 'a',
+    resultField: 'function_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'a'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+});
+
+rqfTeacher.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'adm_dependency',
+    table: '@',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: '@',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}, 'filter').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id','ano_censo'],
+        foreign: ['escola_id','ano_censo'],
+        foreignTable: '@'
+    }
+}, 'dims').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id','ano_censo'],
+        foreign: ['escola_id','ano_censo'],
+        foreignTable: '@'
+    }
+}, 'filter').addValue({
+    name: 'location',
+    table: '@',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'diff_location',
+    table: '@',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: '@',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+});
+
+
+function formatFunction(queryOriginal,reqDims) {
+    delete reqDims.size;
+    delete reqDims.function;
+    let dims = Object.keys(reqDims); //se for = 0, apenas lidamos com a dimensao function. Se for = 1, lidamos com function mais a dimensao q esta nesse array.
+    let name = {
+        qtde_admin: "Administrativos",
+        qtde_servicos_gerais: "Serviços Gerais",
+        qtde_bibliotecario: "Bibliotecário",
+        qtde_saude: "Saúde",
+        qtde_coordenador: "Coordenador",
+        qtde_fono: "Fonoaudiólogo",
+        qtde_nutricionista: "Nutricionista",
+        qtde_psicologo: "Psicólogo",
+        qtde_alimentacao: "Alimentação",
+        qtde_pedagogia: "Pedagogia",
+        qtde_secretario: "Secretário",
+        qtde_seguranca: "Segurança",
+        qtde_monitores: "Monitores",
+        qtde_null: "Não Classificado"
+    }
+    let resultObj = []
+    //Nesse caso apenas precisamos acertar as dimensoes que o banco retorna, ou seja, criando um objeto para cada funcao de funcionario
+    if (dims.length == 0) {
+        queryOriginal.forEach((result) => {
+            Object.keys(result).forEach(function(key,index) {
+                if (key.includes("qtde")) {
+                    let newObj = {
+                        year: result["year"],
+                        function_id: index,
+                        function_name: name[key],
+                        total: result[key]
+                    }
+                    resultObj.push(newObj);
+                }
+            })
+        })
+    }
+    //Nesse caso precisamos copiar o id e name da variavel que está na dimensão junto com funcionarios por função
+    else {
+        queryOriginal.forEach((result) => {
+            Object.keys(result).forEach(function(key,index) {
+                if (key.includes("qtde")) {
+                    let newObj = {
+                        year: result["year"],
+                        function_id: index,
+                        function_name: name[key],
+                        total: result[key]
+                    }
+                    newObj[dims[0] + "_id"] = result[dims[0] + "_id"];
+                    newObj[dims[0] + "_name"] = result[dims[0] + "_name"];
+                    resultObj.push(newObj);
+                }
+            })
+        })
+    }
+
+    return resultObj;
+
+}
+
+function matchQueries(queryTotal, queryPartial) {
+    let match = [];
+    queryTotal.forEach((result) => {
+        let newObj = {};
+        let keys = Object.keys(result);
+        keys.forEach((key) => {
+            newObj[key] = result[key];
+        });
+        let index = keys.indexOf('total');
+        if(index > -1) keys.splice(index, 1);
+        let objMatch = null;
+
+        for(let i = 0; i < queryPartial.length; ++i) {
+            let partial = queryPartial[i];
+            let foundMatch = true;
+            for(let j = 0; j < keys.length; ++j) {
+                let key = keys[j];
+                if(partial[key] !== result[key]) {
+                    foundMatch = false;
+                    break;
+                }
+            }
+            if(foundMatch) {
+                objMatch = partial;
+                break;
+            }
+        }
+
+        if(objMatch) {
+            newObj.total = result.total - objMatch.total;
+            if (newObj.total > 0) {
+                newObj.total_employees = result.total;
+                newObj.total_teachers = objMatch.total
+                match.push(newObj);
+            }
+        }
+    });
+
+    return match;
+}
+
+employeesApp.get('/', rqfSchool.parse(), (req, res, next) => {
+    req.allTeacher = {}
+    req.schoolTable = {}
+
+    if ("function" in req.dims) {
+        delete req.dims.function;
+        req.sql.field('SUM(funcionarios_por_escola.total)', 'qtde_null')
+        .field('funcionarios_por_escola.ano_censo', 'year')
+        .from('funcionarios_por_escola')
+        .group('funcionarios_por_escola.ano_censo')
+        .order('funcionarios_por_escola.ano_censo')
+				.where('funcionarios_por_escola.ano_censo <> 2009 or funcionarios_por_escola.estado_id <> 42')
+    } else {
+        delete req.dims.function;
+        req.sql.field('SUM(funcionarios_por_escola.total)', 'total')
+        .field('funcionarios_por_escola.ano_censo', 'year')
+        .from('funcionarios_por_escola')
+        .group('funcionarios_por_escola.ano_censo')
+        .order('funcionarios_por_escola.ano_censo')
+				.where('funcionarios_por_escola.ano_censo <> 2009 or funcionarios_por_escola.estado_id <> 42')
+    }
+    next();
+
+}, rqfSchool.build(), query, rqfSchool.parse(), id2str.transform(), (req, res, next) => {
+
+    req.allTeacher = req.result;
+    req.resetSql();
+    if ("function" in req.dims) {
+        req.sql.field('SUM(CASE WHEN escola.qt_prof_admin = 88888 THEN 0 ELSE escola.qt_prof_admin END)', 'qtde_admin')
+        .field('SUM(CASE WHEN escola.qtde_prof_servicos_gerais = 88888 THEN 0 ELSE escola.qtde_prof_servicos_gerais END) AS qtde_servicos_gerais')
+        .field('SUM(CASE WHEN escola.qtde_prof_bibliotecario = 88888 THEN 0 ELSE escola.qtde_prof_bibliotecario END)', 'qtde_bibliotecario')
+        .field('SUM(CASE WHEN escola.qtde_prof_saude = 88888 THEN 0 ELSE escola.qtde_prof_saude END)','qtde_saude')
+        .field('SUM(CASE WHEN escola.qtde_prof_coordenador = 88888 THEN 0 ELSE escola.qtde_prof_coordenador END)','qtde_coordenador')
+        .field('SUM(CASE WHEN escola.qtde_prof_fono = 88888 THEN 0 ELSE escola.qtde_prof_fono END)','qtde_fono')
+        .field('SUM(CASE WHEN escola.qtde_prof_nutricionista = 88888 THEN 0 ELSE escola.qtde_prof_nutricionista END)', 'qtde_nutricionista')
+        .field('SUM(CASE WHEN escola.qtde_prof_psicologo = 88888 THEN 0 ELSE escola.qtde_prof_psicologo END)', 'qtde_psicologo')
+        .field('SUM(CASE WHEN escola.qtde_prof_alimentacao = 88888 THEN 0 ELSE escola.qtde_prof_alimentacao END)','qtde_alimentacao')
+        .field('SUM(CASE WHEN escola.qtde_prof_pedagogia = 88888 THEN 0 ELSE escola.qtde_prof_pedagogia END)', 'qtde_pedagogia')
+        .field('SUM(CASE WHEN escola.qtde_prof_secretario = 88888 THEN 0 ELSE escola.qtde_prof_secretario END)','qtde_secretario')
+        .field('SUM(CASE WHEN escola.qtde_prof_seguranca = 88888 THEN 0 ELSE escola.qtde_prof_seguranca END)','qtde_seguranca')
+        .field('SUM(CASE WHEN escola.qtde_prof_monitores = 88888 THEN 0 ELSE escola.qtde_prof_monitores END)', 'qtde_monitores')
+        .field("'Brasil'", 'name')
+        .field('escola.ano_censo', 'year')
+        .from('escola')
+        .group('escola.ano_censo')
+        .order('escola.ano_censo')
+        .where('(escola.situacao_funcionamento_pareada = 1) AND (escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1) AND (escola.dependencia_adm_id = 2 OR escola.dependencia_adm_id = 3 OR escola.dependencia_adm_id = 4) and ano_censo >= 2019');
+        delete req.dims.function;
+    } else {
+        req.sql.field('SUM(CASE WHEN escola.qt_prof_admin = 88888 THEN 0 ELSE escola.qt_prof_admin END) + SUM(CASE WHEN escola.qtde_prof_servicos_gerais = 88888 THEN 0 ELSE escola.qtde_prof_servicos_gerais END) + SUM(CASE WHEN escola.qtde_prof_bibliotecario = 88888 THEN 0 ELSE escola.qtde_prof_bibliotecario END) + SUM(CASE WHEN escola.qtde_prof_saude = 88888 THEN 0 ELSE escola.qtde_prof_saude END) + SUM(CASE WHEN escola.qtde_prof_coordenador = 88888 THEN 0 ELSE escola.qtde_prof_coordenador END) + SUM(CASE WHEN escola.qtde_prof_fono = 88888 THEN 0 ELSE escola.qtde_prof_fono END) + SUM(CASE WHEN escola.qtde_prof_nutricionista = 88888 THEN 0 ELSE escola.qtde_prof_nutricionista END) + SUM(CASE WHEN escola.qtde_prof_psicologo = 88888 THEN 0 ELSE escola.qtde_prof_psicologo END) + SUM(CASE WHEN escola.qtde_prof_alimentacao = 88888 THEN 0 ELSE escola.qtde_prof_alimentacao END) + SUM(CASE WHEN escola.qtde_prof_pedagogia = 88888 THEN 0 ELSE escola.qtde_prof_pedagogia END) + SUM(CASE WHEN escola.qtde_prof_secretario = 88888 THEN 0 ELSE escola.qtde_prof_secretario END) + SUM(CASE WHEN escola.qtde_prof_seguranca = 88888 THEN 0 ELSE escola.qtde_prof_seguranca END) + SUM(CASE WHEN escola.qtde_prof_monitores = 88888 THEN 0 ELSE escola.qtde_prof_monitores END)', 'total')
+        .field("'Brasil'", 'name')
+        .field('escola.ano_censo', 'year')
+        .from('escola')
+        .group('escola.ano_censo')
+        .order('escola.ano_censo')
+        .where('(escola.situacao_funcionamento_pareada = 1) AND (escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1) AND (escola.dependencia_adm_id = 2 OR escola.dependencia_adm_id = 3 OR escola.dependencia_adm_id = 4) and ano_censo >= 2019');
+    }
+    next();
+
+}, rqfSchool.build(), query, rqfSchool.parse(), id2str.transform(), addMissing(rqfSchool), (req, res, next) => {
+
+    if ("function" in req.dims) {
+        let aux_employes = formatFunction(req.result, req.dims);
+        req.allTeacher = formatFunction(req.allTeacher, req.dims);
+        req.schoolTable = aux_employes;
+    } else {
+        req.schoolTable = req.result
+    }
+
+    if (req.filter.min_year <= 2018 && req.filter.max_year <= 2018) {
+        let aux_employees = req.allTeacher;
+        req.result = aux_employees;
+    } else if (req.filter.min_year >= 2019 && req.filter.max_year >= 2019) {
+        req.result = req.schoolTable;
+    } else if (req.filter.min_year <= 2018 && req.filter.max_year >= 2019) {
+        let aux_employees = req.allTeacher;
+        req.result = aux_employees.concat(req.schoolTable);
+    }
+    next();
+}, response('employees'));
+
+module.exports = employeesApp;
diff --git a/src/libs/routes_v2/enrollment.js b/src/libs/routes_v2/enrollment.js
new file mode 100644
index 00000000..87259385
--- /dev/null
+++ b/src/libs/routes_v2/enrollment.js
@@ -0,0 +1,1204 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+const { result } = require('lodash');
+
+const enrollmentApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+enrollmentApp.use(cache('15 day'));
+
+let rqf = new ReqQueryFields();
+
+// Complete range of the enrollments dataset.
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+enrollmentApp.get('/year_range', (req, res, next) => {
+    req.sql.from('matricula')
+    .field('MIN(matricula.ano_censo)', 'start_year')
+    .field('MAX(matricula.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+enrollmentApp.get('/years', (req, res, next) => {
+    req.sql.from('matricula')
+    .field('DISTINCT matricula.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+enrollmentApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'matricula\'');
+    next();
+}, query, response('source'));
+
+enrollmentApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+enrollmentApp.get('/diff_location', (req, res, next) => {
+    req.result = [
+        {id: 0, name: "A escola não está em localidade diferenciada"},
+        {id: 1, name: "Área de assentamento"},
+        {id: 2, name: "Terra indígena"},
+        {id: 3, name: "Terra remanescente de quilombos"},
+    ];
+    next();
+}, response('diff_location'));
+
+// Returns all school years available
+enrollmentApp.get('/school_year', (req, res, next) => {
+    req.result = [];
+    for(let i = 11; i <= 71; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.schoolYear(i)
+        };
+
+        if(obj.name !== id2str.schoolYear(99)) {
+            req.result.push(obj);
+        }
+    }
+    req.result.push({
+        id: 99,
+        name: id2str.schoolYear(99)
+    });
+    next();
+}, response('school_year'));
+
+// Returns all school years available
+enrollmentApp.get('/education_level', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 74; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.educationLevel(i)
+        };
+
+        if(obj.name !== id2str.educationLevel(99)) {
+            req.result.push(obj);
+        }
+    }
+    req.result.push({
+        id: 99,
+        name: id2str.educationLevel(99)
+    });
+    next();
+}, response('education_level'));
+
+// Returns all school years available
+enrollmentApp.get('/education_level_mod', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 12; ++i) {
+        if (i == 3 || i == 6)
+            continue;
+        
+        req.result.push({
+            id: i,
+            name: id2str.educationLevelMod(i)
+        });
+    }
+    req.result.push({
+        id: 99,
+        name: id2str.educationLevelMod(99)
+    });
+    next();
+}, response('education_level_mod'));
+
+enrollmentApp.get('/education_level_short', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não classificada'},
+        {id: 1, name: 'Creche'},
+        {id: 2, name: 'Pré-Escola'},
+        {id: 3, name: 'Ensino Fundamental - anos iniciais'},
+        {id: 4, name: 'Ensino Fundamental - anos finais'},
+        {id: 5, name: 'Ensino Médio'},
+        {id: 6, name: 'EJA'},
+        {id: 7, name: 'EE exclusiva'}
+    ];
+    next();
+}, response('education_level_short'));
+
+// Returns all adm dependencies
+enrollmentApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+enrollmentApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 8; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+// Return genders
+enrollmentApp.get('/gender', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Masculino'},
+        {id: 2, name: 'Feminino'}
+    ];
+    next();
+}, response('gender'));
+
+// Return ethnic group
+enrollmentApp.get('/ethnic_group', (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <=5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ethnicGroup(i)
+        });
+    }
+    next();
+}, response('ethnic_group'));
+
+enrollmentApp.get('/period', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.period(i)
+        });
+    }
+    req.result.push({
+        id: 99,
+        name: id2str.period(99)
+    });
+    next();
+}, response('period'));
+
+// Returns integral-time avaible
+enrollmentApp.get('/integral_time', (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <= 2; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.integralTime(i)
+        });
+    }
+    next();
+}, response('integral_time'));
+
+enrollmentApp.get('/special_class', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não Declarado'},
+        {id: 0, name: 'Não'},
+        {id: 1, name: 'Sim'}
+    ];
+    next();
+}, response('special_class'));
+
+enrollmentApp.get('/pee', (req, res, next) => {
+    req.result = [
+        {id: true, name: id2str.booleanVariable(true)},
+        {id: false, name: id2str.booleanVariable(false)}
+    ];
+    next();
+}, response('pee'))
+
+enrollmentApp.get('/pee_por_categoria', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 14; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.peePorCategoria(i)
+        });
+    }
+    next();
+}, response('pee_por_categoria'));
+
+enrollmentApp.get('/age_range_all', (req, res, next) => {
+    req.result = [
+        {id: 1, name: '0 a 3 anos'},
+        {id: 2, name: '4 a 5 anos'},
+        {id: 3, name: '6 a 10 anos'},
+        {id: 4, name: '11 a 14 anos'},
+        {id: 5, name: '15 a 17 anos'},
+        {id: 6, name: '18 a 24 anos'},
+        {id: 7, name: '25 a 29 anos'},
+        {id: 8, name: '30 a 40 anos'},
+        {id: 9, name: '41 a 50 anos'},
+        {id: 10, name: '51 a 64 anos'},
+        {id: 11, name: 'Mais que 64 anos'}
+    ];
+    next();
+}, response('age_range_all'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'adm_dependency',
+    table: 'matricula',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'matricula',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'school_year',
+    table: 'matricula',
+    tableField: 'serie_ano_id',
+    resultField: 'school_year_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'serie_ano_id'
+    }
+}).addValue({
+    name: 'education_level',
+    table: 'matricula',
+    tableField: 'etapa_ensino_id',
+    resultField: 'education_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_ensino_id'
+    }
+}).addValue({
+    name: 'education_level_mod',
+    table: 'matricula',
+    tableField: 'etapas_mod_ensino_segmento_id',
+    resultField: 'education_level_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapas_mod_ensino_segmento_id'
+    }
+}).addValue({
+    name: 'education_level_short',
+    table: 'matricula',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'matricula'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'matricula'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'matricula'
+    }
+}).addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'matricula'
+    }
+}, 'dims').addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: 'nome',
+    resultField: 'state_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'matricula'
+    }
+}, 'filter').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'matricula'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'matricula'
+    }
+}, 'filter').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'matricula'
+    }
+}, 'dims').addValueToField({
+    name: 'locale_id',
+    table: 'matricula',
+    tableField: 'localizacao_id',
+    resultField: 'locale_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}, 'dims').addValueToField({
+    name: 'school_id',
+    table: 'escola',
+    tableField: 'id',
+    resultField: 'school_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'matricula'
+    }
+}, 'dims').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'matricula'
+    }
+}, 'filter').addValue({
+    name: 'location',
+    table: 'matricula',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'diff_location',
+    table: 'matricula',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'matricula',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'matricula',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'matricula',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'gender',
+    table: 'matricula',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'ethnic_group',
+    table: 'matricula',
+    tableField: 'cor_raca_id',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca_id'
+    }
+}).addValue({
+    name: 'period',
+    table: 'matricula',
+    tableField: 'turma_turno_id',
+    resultField: 'period_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'turma_turno_id'
+    }
+}).addValue({
+  name:'integral_time',
+  table: 'matricula',
+  tableField: 'tempo_integral',
+  resultField: 'integral_time_id',
+  where: {
+      relation: '=',
+      type: 'integer',
+      field: 'tempo_integral'
+  }
+}).addValue({
+  name:'age_range_all',
+  table: 'matricula',
+  tableField: 'faixa_etaria_31_03',
+  resultField: 'age_range_all_id',
+  where: {
+      relation: '=',
+      type: 'integer',
+      field: 'faixa_etaria_31_03'
+  }
+}).addValue({
+  name:'special_class',
+  table: 'matricula',
+  tableField: 'exclusiva_especial',
+  resultField: 'special_class_id',
+  where: {
+      relation: '=',
+      type: 'boolean',
+      field: 'exclusiva_especial'
+  }
+}).addValueToField({
+    name: 'period_not',
+    table: 'matricula',
+    tableField: 'turma_turno_id',
+    resultField: 'period_id',
+    where: {
+        relation: '<>',
+        type: 'integer',
+        field: 'turma_turno_id'
+    }
+}, 'filter')
+.addValue({
+    name: 'low_vision',
+    table: 'matricula',
+    tableField: 'baixa_visao',
+    resultField: 'low_vision',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'baixa_visao'
+    } 
+}).addValue({
+    name: 'blindness',
+    table: 'matricula',
+    tableField: 'cegueira',
+    resultField: 'blindness',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'cegueira'
+    }
+}).addValue({
+    name: 'deafness',
+    table: 'matricula',
+    tableField: 'surdez',
+    resultField: 'deafness',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'surdez'
+    }
+}).addValue({
+    name: 'hearing_deficiency',
+    table: 'matricula',
+    tableField: 'deficiencia_auditiva',
+    resultField: 'hearing_deficiency',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'deficiencia_auditiva'
+    }
+}).addValue({
+    name: 'deafblindness',
+    table: 'matricula',
+    tableField: 'surdo_cegueira',
+    resultField: 'deafblindness',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'surdo_cegueira'
+    }
+}).addValue({
+    name: 'physical_disability',
+    table: 'matricula',
+    tableField: 'deficiencia_fisica',
+    resultField: 'physical_disability',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'deficiencia_fisica'
+    }
+}).addValue({
+    name: 'intellectual_disability',
+    table: 'matricula',
+    tableField: 'deficiencia_intelectual',
+    resultField: 'intellectual_disability',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'deficiencia_intelectual'
+    }
+}).addValue({
+    name: 'multiple_disabilities',
+    table: 'matricula',
+    tableField: 'deficiencia_multiplas',
+    resultField: 'multiple_disabilities',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'deficiencia_multiplas'
+    }
+}).addValue({
+    name: 'autism',
+    table: 'matricula',
+    tableField: 'autismo',
+    resultField: 'autism',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'autismo'
+    }
+}).addValue({
+    name: 'autism_spectrum_disorder',
+    table: 'matricula',
+    tableField: 'transtorno_espectro_autista',
+    resultField: 'autism_spectrum_disorder',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'transtorno_espectro_autista'
+    }
+}).addValue({
+    name: 'asperger_syndrom',
+    table: 'matricula',
+    tableField: 'sindrome_asperger',
+    resultField: 'asperger_syndrom',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'sindrome_asperger'
+    }
+}).addValue({
+    name: 'rett_syndrom',
+    table: 'matricula',
+    tableField: 'sindrome_rett',
+    resultField: 'rett_syndrom',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'sindrome_rett'
+    }
+}).addValue({
+    name: 'childhood_desintegrative_disorder',
+    table: 'matricula',
+    tableField: 'transtorno_desintegrativo_da_infancia',
+    resultField: 'childhood_desintegrative_disorder',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'transtorno_desintegrativo_da_infancia'
+    }
+}).addValue({
+    name: 'supergifted',
+    table: 'matricula',
+    tableField: 'superdotado',
+    resultField: 'supergifted',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'superdotado'
+    }
+}).addValue({
+    name: 'pee',
+    table: 'matricula',
+    tableField: 'possui_necessidade_especial',
+    resultField: 'pee_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'possui_necessidade_especial'
+    }
+}).addValue({
+    name: 'pee_por_categoria',
+    table: 'matricula',
+    tableField: 'possui_necessidade_especial',
+    resultField: 'pee_por_categoria',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'possui_necessidade_especial'
+    }
+});
+
+enrollmentApp.get('/', rqf.parse(), (req, res, next) => {
+    if('pee_por_categoria' in req.dims){
+        delete req.dims.pee_por_categoria
+        req.pee_por_categoria = true
+        req.sql.field('SUM(CASE WHEN cegueira = true THEN 1 ELSE 0 END)', 'Cegueira')
+        .field('SUM(CASE WHEN baixa_visao = true THEN 1 ELSE 0 END)', 'Baixa visão')
+        .field('SUM(CASE WHEN surdez = true THEN 1 ELSE 0 END)', 'Surdez')
+        .field('SUM(CASE WHEN deficiencia_auditiva = true THEN 1 ELSE 0 END)', 'Deficiência auditiva')
+        .field('SUM(CASE WHEN surdo_cegueira = true THEN 1 ELSE 0 END)', 'Surdocegueira')
+        .field('SUM(CASE WHEN deficiencia_fisica = true THEN 1 ELSE 0 END)', 'Deficiência física')
+        .field('SUM(CASE WHEN deficiencia_intelectual = true THEN 1 ELSE 0 END)', 'Deficiência intelectual')
+        .field('SUM(CASE WHEN deficiencia_multiplas = true THEN 1 ELSE 0 END)', 'Deficiências múltiplas')
+        .field('SUM(CASE WHEN autismo = true THEN 1 ELSE 0 END)', 'Autismo')
+        .field('SUM(CASE WHEN transtorno_espectro_autista = true THEN 1 ELSE 0 END)', 'Transtorno do Espectro Autista (TEA)')
+        .field('SUM(CASE WHEN sindrome_asperger = true THEN 1 ELSE 0 END)', 'Síndrome de Asperger')
+        .field('SUM(CASE WHEN sindrome_rett = true THEN 1 ELSE 0 END)', 'Síndrome de Rett')
+        .field('SUM(CASE WHEN transtorno_desintegrativo_da_infancia = true THEN 1 ELSE 0 END)', 'Transtorno desintegrativo da infância')
+        .field('SUM(CASE WHEN superdotado = true THEN 1 ELSE 0 END)', 'Altas habilidades / Superdotação')
+        .field('matricula.ano_censo', 'year')
+        .from('matricula')
+        .group('matricula.ano_censo')
+        .order('matricula.ano_censo')
+        .where('((matricula.tipo<=3 OR matricula.tipo IS NULL) AND (matricula.tipo_atendimento_turma IS NULL OR matricula.tipo_atendimento_turma <= 2))');
+    }
+    else{
+        req.sql.field('COUNT(*)', 'total')
+        .field('matricula.ano_censo', 'year')
+        .from('matricula')
+        .group('matricula.ano_censo')
+        .order('matricula.ano_censo')
+        .where('((matricula.tipo<=3 OR matricula.tipo IS NULL) AND (matricula.tipo_atendimento_turma IS NULL OR matricula.tipo_atendimento_turma <= 2))');
+    }
+    next();
+}, rqf.build(), query, id2str.transform(false), (req, res, next) => {
+    if(req.pee_por_categoria === true){
+        let result = req.result;
+        let result_total = [];
+        for (var j = 0;j < result.length;j++){
+            let result_parcial = result[j];
+            for (var i in result_parcial){
+                if(i !== 'year'){
+                    let obj = {};
+                    obj.total = result_parcial[i];
+                    i = i.replace(/"/g, '');
+                    obj.pee_por_categoria_name = i;
+                    obj.year = result_parcial.year;
+                    result_total.push(obj);
+                }
+            }
+        }
+        req.result= result_total;
+    }
+    next();
+}, response('enrollment'));
+
+enrollmentApp.get('/diagnosis', rqf.parse(), (req, res, next) => {
+    req.dims = {};
+    req.dims.state = true;
+    req.dims.city = true;
+    req.dims.school_year = true;
+    req.dims.location = true;
+    req.dims.adm_dependency_detailed = true;
+
+    req.sql.field('COUNT(*)', 'total')
+    .field("'Brasil'", 'name')
+    .field('matricula.ano_censo', 'year')
+    .from('matricula')
+    .group('matricula.ano_censo')
+    .order('matricula.ano_censo')
+    .where('matricula.tipo<=3');
+
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    let enrollments = req.result;
+
+    // Gera a relação etapa de ensino X ano escolar
+    let educationSchoolYear = {};
+    for(let i = 10; i < 80; ++i) {
+        if(id2str.schoolYear(i) !== id2str.schoolYear(99)) {
+            let educationLevelId = Math.floor(i/10);
+            educationSchoolYear[i] = {
+                id: educationLevelId,
+                name: id2str.educationLevelShort(educationLevelId),
+            };
+        }
+    }
+
+    let result = [];
+    let educationLevelSet = new Set();
+    let schoolYearSet = new Set();
+    let i = 0;
+    while(i < enrollments.length) {
+        let enrollment = enrollments[i];
+        if(!educationSchoolYear[enrollment.school_year_id]) {
+            ++i;
+            continue;
+        }
+        let educationLevelHash = '' + enrollment.year + educationSchoolYear[enrollment.school_year_id].id + enrollment.city_id;
+        let schoolYearHash = '' + enrollment.year + enrollment.school_year_id + enrollment.city_id;
+
+        let currentEducation = null;
+        // Busca ou cria a etapa de ensino adequada
+        if(educationLevelSet.has(educationLevelHash)) {
+            let j = 0;
+            let edu = result[j];
+            while(j < result.length && (edu.year != enrollment.year || edu.education_level_school_year_id != educationSchoolYear[enrollment.school_year_id].id)) {
+                ++j;
+                edu = result[j];
+            }
+            if(j >= result.length) --j;
+            edu = result[j];
+
+            currentEducation = edu;
+        } else {
+            educationLevelSet.add(educationLevelHash);
+            let obj = {
+                year: enrollment.year,
+                name: enrollment.name,
+                state_id: enrollment.state_id,
+                state_name: enrollment.state_name,
+                city_id: enrollment.city_id,
+                city_name: enrollment.city_name,
+                education_level_school_year_id: educationSchoolYear[enrollment.school_year_id].id,
+                education_level_school_year_name: educationSchoolYear[enrollment.school_year_id].name,
+                total: 0,
+                adm_dependencies: [
+                    {
+                        adm_dependency_detailed_id: enrollment.adm_dependency_detailed_id,
+                        adm_dependency_detailed_name: enrollment.adm_dependency_detailed_name,
+                        total: 0
+                    }
+                ],
+                locations: [
+                    {
+                        location_id: enrollment.location_id,
+                        location_name: enrollment.location_name,
+                        total: 0
+                    }
+                ]
+            };
+
+            result.push(obj);
+            currentEducation = obj;
+        }
+
+        let currentSchoolYear = null;
+        // Busca ou cria a série adequada
+        if(schoolYearSet.has(schoolYearHash)) {
+            let j = 0;
+            let edu = result[j];
+            while(j < result.length && (edu.year != enrollment.year || edu.education_level_school_year_id != enrollment.school_year_id)) {
+                ++j;
+                edu = result[j];
+            }
+            if(j >= result.length) --j;
+            edu = result[j];
+
+            currentSchoolYear = edu;
+        } else {
+            schoolYearSet.add(schoolYearHash);
+            let obj = {
+                year: enrollment.year,
+                name: enrollment.name,
+                state_id: enrollment.state_id,
+                state_name: enrollment.state_name,
+                city_id: enrollment.city_id,
+                city_name: enrollment.city_name,
+                education_level_school_year_id: enrollment.school_year_id,
+                education_level_school_year_name: enrollment.school_year_name,
+                total: 0,
+                adm_dependencies: [
+                    {
+                        adm_dependency_detailed_id: enrollment.adm_dependency_detailed_id,
+                        adm_dependency_detailed_name: enrollment.adm_dependency_detailed_name,
+                        total: 0
+                    }
+                ],
+                locations: [
+                    {
+                        location_id: enrollment.location_id,
+                        location_name: enrollment.location_name,
+                        total: 0
+                    }
+                ]
+            };
+
+            result.push(obj);
+            currentSchoolYear = obj;
+        }
+
+        // Adiciona ao total
+        currentEducation.total += enrollment.total;
+        currentSchoolYear.total += enrollment.total;
+
+        // Adiciona ao total da dependência administrativa
+        let admDependencyIndex = 0;
+        let admDependency = currentEducation.adm_dependencies[admDependencyIndex];
+        while (admDependencyIndex < currentEducation.adm_dependencies.length && enrollment.adm_dependency_detailed_id > admDependency.adm_dependency_detailed_id) {
+            ++admDependencyIndex;
+            admDependency = currentEducation.adm_dependencies[admDependencyIndex];
+        }
+        if(admDependencyIndex >= currentEducation.adm_dependencies.length || admDependency.adm_dependency_detailed_id != enrollment.adm_dependency_detailed_id) { // não encontrou
+            let obj = {
+                adm_dependency_detailed_id: enrollment.adm_dependency_detailed_id,
+                adm_dependency_detailed_name: enrollment.adm_dependency_detailed_name,
+                total: 0
+            }
+            currentEducation.adm_dependencies.splice(admDependencyIndex, 0, obj);
+            admDependency = obj;
+        }
+        admDependency.total += enrollment.total;
+
+        admDependencyIndex = 0;
+        admDependency = currentSchoolYear.adm_dependencies[admDependencyIndex];
+        while (admDependencyIndex < currentSchoolYear.adm_dependencies.length && enrollment.adm_dependency_detailed_id > admDependency.adm_dependency_detailed_id) {
+            ++admDependencyIndex;
+            admDependency = currentSchoolYear.adm_dependencies[admDependencyIndex];
+        }
+        if(admDependencyIndex >= currentSchoolYear.adm_dependencies.length || admDependency.adm_dependency_detailed_id != enrollment.adm_dependency_detailed_id) { // não encontrou
+            let obj = {
+                adm_dependency_detailed_id: enrollment.adm_dependency_detailed_id,
+                adm_dependency_detailed_name: enrollment.adm_dependency_detailed_name,
+                total: 0
+            }
+            currentSchoolYear.adm_dependencies.splice(admDependencyIndex, 0, obj);
+            admDependency = obj;
+        }
+        admDependency.total += enrollment.total;
+
+        // Adiciona ao total da localidade
+        let locationIndex = 0;
+        let location = currentEducation.locations[locationIndex];
+        while (locationIndex < currentEducation.locations.length && enrollment.location_id > location.location_id) {
+            ++locationIndex;
+            location = currentEducation.locations[locationIndex];
+        }
+        if(locationIndex >= currentEducation.locations.length || location.location_id != enrollment.location_id) {
+            let obj = {
+                location_id: enrollment.location_id,
+                location_name: enrollment.location_name,
+                total: 0
+            }
+            currentEducation.locations.splice(locationIndex, 0, obj);
+            location = obj;
+        }
+        location.total += enrollment.total;
+
+        locationIndex = 0;
+        location = currentSchoolYear.locations[locationIndex];
+        while (locationIndex < currentSchoolYear.locations.length && enrollment.location_id > location.location_id) {
+            ++locationIndex;
+            location = currentSchoolYear.locations[locationIndex];
+        }
+        if(locationIndex >= currentSchoolYear.locations.length || location.location_id != enrollment.location_id) {
+            let obj = {
+                location_id: enrollment.location_id,
+                location_name: enrollment.location_name,
+                total: 0
+            }
+            currentSchoolYear.locations.splice(locationIndex, 0, obj);
+            location = obj;
+        }
+        location.total += enrollment.total;
+
+        ++i;
+    }
+
+    req.result = result;
+
+    next();
+}, response('enrollment_diagnosis'));
+
+enrollmentApp.get('/projection', rqf.parse(), (req, res, next) => {
+    req.dims = {};
+    req.dims.state = true;
+    req.dims.city = true;
+    req.dims.location = true;
+    req.dims.school_year = true;
+    req.dims.adm_dependency = true;
+    req.dims.period = true;
+    req.filter.adm_dependency = [1,2,3];
+
+    req.sql.field('COUNT(*)', 'total')
+    .field("'Brasil'", 'name')
+    .field('matricula.ano_censo', 'year')
+    .from('matricula')
+    .group('matricula.ano_censo')
+    .order('matricula.ano_censo')
+    .where('matricula.tipo<=3');
+
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    let enrollments = req.result;
+
+    // Gera a relação etapa de ensino X ano escolar
+    let educationSchoolYear = {};
+    for(let i = 10; i < 80; ++i) {
+        if(id2str.schoolYear(i) !== id2str.schoolYear(99)) {
+            let educationLevelId = Math.floor(i/10);
+            educationSchoolYear[i] = {
+                id: educationLevelId,
+                name: id2str.educationLevelShort(educationLevelId),
+            };
+        }
+    }
+
+    let result = [];
+    let educationLevelSet = new Set();
+    let schoolYearSet = new Set();
+    let i = 0;
+    while(i < enrollments.length) {
+        let enrollment = enrollments[i];
+        if(!educationSchoolYear[enrollment.school_year_id]) {
+            ++i;
+            continue;
+        }
+        let educationLevelHash = '' + enrollment.year + educationSchoolYear[enrollment.school_year_id].id + enrollment.city_id;
+        let schoolYearHash = '' + enrollment.year + enrollment.school_year_id + enrollment.city_id;
+
+        let currentEducation = null;
+        // Busca ou cria a etapa de ensino adequada
+        if(educationLevelSet.has(educationLevelHash)) {
+            let j = 0;
+            let edu = result[j];
+            while(j < result.length && (edu.year != enrollment.year || edu.education_level_school_year_id != educationSchoolYear[enrollment.school_year_id].id)) {
+                ++j;
+                edu = result[j];
+            }
+            if((j >= result.length)) --j;
+            edu = result[j];
+
+            currentEducation = edu;
+        } else {
+            educationLevelSet.add(educationLevelHash);
+            let obj = {
+                year: enrollment.year,
+                name: enrollment.name,
+                state_id: enrollment.state_id,
+                state_name: enrollment.state_name,
+                city_id: enrollment.city_id,
+                city_name: enrollment.city_name,
+                education_level_school_year_id: educationSchoolYear[enrollment.school_year_id].id,
+                education_level_school_year_name: educationSchoolYear[enrollment.school_year_id].name,
+                urban_day_total: 0,
+                urban_night_total: 0,
+                rural_day_total: 0,
+                rural_night_total: 0
+            };
+            result.push(obj);
+            currentEducation = obj;
+        }
+
+        let currentSchoolYear = null;
+        // Busca ou cria a série adequada
+        if(schoolYearSet.has(schoolYearHash)) {
+            let j = 0;
+            let edu = result[j];
+            while(j < result.length && (edu.year != enrollment.year || edu.education_level_school_year_id != enrollment.school_year_id)){
+                ++j;
+                edu = result[j];
+            }
+            if(j >= result.length) --j;
+            edu = result[j];
+
+            currentSchoolYear = edu;
+        } else {
+            schoolYearSet.add(schoolYearHash);
+            let obj = {
+                year: enrollment.year,
+                name: enrollment.name,
+                state_id: enrollment.state_id,
+                state_name: enrollment.state_name,
+                city_id: enrollment.city_id,
+                city_name: enrollment.city_name,
+                education_level_school_year_id: enrollment.school_year_id,
+                education_level_school_year_name: enrollment.school_year_name,
+                urban_day_total: 0,
+                urban_night_total: 0,
+                rural_day_total: 0,
+                rural_night_total: 0
+            };
+
+            result.push(obj);
+            currentSchoolYear = obj;
+        }
+
+        if(enrollment.location_id == 1) {
+            if(enrollment.period_id < 3) {
+                currentEducation.urban_day_total += enrollment.total;
+                currentSchoolYear.urban_day_total += enrollment.total;
+            } else {
+                currentEducation.urban_night_total += enrollment.total;
+                currentSchoolYear.urban_night_total += enrollment.total;
+            }
+        } else {
+            if(enrollment.period_id < 3) {
+                currentEducation.rural_day_total += enrollment.total;
+                currentSchoolYear.rural_day_total += enrollment.total;
+            } else {
+                currentEducation.rural_night_total += enrollment.total;
+                currentSchoolYear.rural_night_total += enrollment.total;
+            }
+        }
+
+        ++i;
+    }
+
+    req.result = result;
+
+    next();
+}, response('enrollment_projection'));
+
+module.exports = enrollmentApp;
diff --git a/src/libs/routes_v2/enrollmentProjection.js b/src/libs/routes_v2/enrollmentProjection.js
new file mode 100644
index 00000000..4dd92e5d
--- /dev/null
+++ b/src/libs/routes_v2/enrollmentProjection.js
@@ -0,0 +1,239 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const enrollmentProjectionApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+let rqf = new ReqQueryFields();
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'projecao_matricula_por_dependencia'
+    }
+}, 'dims').addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: 'nome',
+    resultField: 'state_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'projecao_matricula_por_dependencia'
+    }
+}, 'filter').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'projecao_matricula_por_dependencia'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'projecao_matricula_por_dependencia'
+    }
+}, 'filter').addValue({
+    name: 'min_year',
+    table: 'projecao_matricula_por_dependencia',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'projecao_matricula_por_dependencia',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'projecao_matricula_por_dependencia',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+});
+
+enrollmentProjectionApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.field("'Brasil'", 'name')
+        .field('SUM(projecao_matricula_por_dependencia.urbano_dia_total)', 'urban_day_total')
+        .field('SUM(projecao_matricula_por_dependencia.urbano_noite_total)', 'urban_night_total')
+        .field('SUM(projecao_matricula_por_dependencia.rural_dia_total)', 'rural_day_total')
+        .field('SUM(projecao_matricula_por_dependencia.rural_noite_total)', 'rural_night_total')
+        .field('projecao_matricula_por_dependencia.etapa_ensino_escola_ano_id', 'education_level_school_year_id')
+        .field('projecao_matricula_por_dependencia.ano_censo', 'year')
+        .from('projecao_matricula_por_dependencia')
+        .where('projecao_matricula_por_dependencia.etapa_ensino_escola_ano_id <> 7 AND projecao_matricula_por_dependencia.etapa_ensino_escola_ano_id < 71')
+        .group('projecao_matricula_por_dependencia.etapa_ensino_escola_ano_id')
+        .group('projecao_matricula_por_dependencia.ano_censo')
+        .order('projecao_matricula_por_dependencia.ano_censo')
+        .order('projecao_matricula_por_dependencia.etapa_ensino_escola_ano_id');
+
+    next();
+}, query, id2str.transform(), (req, res, next) => {
+
+    let result = [];
+    let i = 0;
+    let j = 1;
+    let base_year = req.result[0].year;
+
+    let hashSet = new Set();
+    let atual_city_name;
+    let atual_city_id;
+    let atual_state_name;
+    let atual_state_id;
+    while (i < req.result.length || j <= 63) {      // Adciona séries não existentes no banco com 0 nos totais.
+        let atual = req.result[i];
+        if (j > 63) {       // Caso j passou da última série existente, mudamos de dimensão.
+            j = 1;
+
+            if (base_year !== atual.year) {
+                base_year = atual.year;
+                hashSet = new Set();
+            }
+        }
+        if (id2str.educationLevelSchoolYear(j) === id2str.educationLevelSchoolYear(99) || j === 7) {
+            j++;
+            continue;
+        }
+
+        if (j == 1) {
+            let hash = ""
+            if ('state' in req.dims)
+                hash += atual.state_id;
+            if ('city' in req.dims)
+                hash += atual.city_id;
+
+            if (!hashSet.has(hash)) {
+                hashSet.add(hash);
+                atual_city_id = atual.city_id;
+                atual_city_name = atual.city_name;
+                atual_state_id = atual.state_id;
+                atual_state_name = atual.state_name;
+            }
+        }
+
+        if (atual !== undefined && atual.education_level_school_year_id === j) {   // Série existe.
+            atual.urban_day_total = parseInt(atual.urban_day_total, 10);
+            atual.urban_night_total = ((atual.education_level_school_year_id >= 3 && atual.education_level_school_year_id < 10) || (atual.education_level_school_year_id >= 30)) ? parseInt(atual.urban_night_total, 10) : 0;   //Não conta matrículas noturnas da pré-escola e da creche
+            atual.rural_day_total = parseInt(atual.rural_day_total, 10);
+            atual.rural_night_total = ((atual.education_level_school_year_id >= 3 && atual.education_level_school_year_id < 10) || (atual.education_level_school_year_id >= 30)) ? parseInt(atual.rural_night_total, 10) : 0;
+            result.push(atual);
+
+            i++;
+        }
+        else {      //  Série não existe, adcionamos 0 ao resultado.
+            let base_result = {
+                name: req.result[0].name,
+                urban_day_total: 0,
+                urban_night_total: 0,
+                rural_day_total: 0,
+                rural_night_total: 0,
+                education_level_school_year_id: j,
+                year: base_year,
+                education_level_school_year_name: id2str.educationLevelSchoolYear(j)
+            };
+
+            if ('city' in req.dims) {           // adciona os campos de cidade e/ou estado
+                base_result.city_id = atual_city_id;
+                base_result.city_name = atual_city_name;
+            }
+            if ('state' in req.dims) {
+                base_result.state_id = atual_state_id;
+                base_result.state_name = atual_state_name;
+            }
+
+            result.push(base_result)
+        }
+        j++;
+    }
+
+    req.result = result;
+    next();
+}, response('enrollment_projection'));
+
+module.exports = enrollmentProjectionApp;
diff --git a/src/libs/routes_v2/financial.js b/src/libs/routes_v2/financial.js
new file mode 100644
index 00000000..dab9dfe8
--- /dev/null
+++ b/src/libs/routes_v2/financial.js
@@ -0,0 +1,186 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const financialApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+financialApp.get('/year_range', (req, res, next) => {
+    req.sql.from('indicadores_financeiros')
+    .field('MIN(indicadores_financeiros.ano_censo)', 'start_year')
+    .field('MAX(indicadores_financeiros.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+financialApp.get('/years', (req, res, next) => {
+    req.sql.from('indicadores_financeiros')
+    .field('DISTINCT indicadores_financeiros.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+financialApp.get('/sphere_adm', (req, res, next) => {
+    req.result = [
+        {id: 1, name: "1"},
+        {id: 2, name: "2"}
+    ]
+    next();
+}, response('sphere_adm'));
+
+financialApp.get('/financial_data', (req, res, next) => {
+    req.sql.from('indicadores_financeiros')
+    .field('DISTINCT indicadores_financeiros.dados_financeiros', 'financial_data');
+    next();
+}, query, response('financial_data'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['sigla', 'id'],
+    resultField: ['sigla_uf', 'cod_uf'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'indicadores_financeiros'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'indicadores_financeiros'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'indicadores_financeiros',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'indicadores_financeiros',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'indicadores_financeiros',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'indicadores_financeiros',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'sphere_adm',
+    table: 'indicadores_financeiros',
+    tableField: 'esfera_adm',
+    resultField: 'sphere_adm_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'esfera_adm'
+    }
+}).addValue({
+    name: 'city',
+    table: 'indicadores_financeiros',
+    tableField: 'municipio_id',
+    resultField: 'city_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id'
+    }
+}).addValue({
+    name: 'financial_data',
+    table: 'indicadores_financeiros',
+    tableField: 'dados_financeiros',
+    resultField: 'financial_data_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dados_financeiros'
+    }
+});
+
+financialApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+		if ("state" in req.filter) {
+	    req.sql.from('indicadores_financeiros')
+	    .field('indicadores_financeiros.estado_id', 'state_id')
+	    .field('indicadores_financeiros.ano_censo', 'year')
+	    .field('estado.sigla', 'state_abbreviation')
+	    .field('indicadores_financeiros.valor', 'valor')
+	    .field('indicadores_financeiros.esfera_adm', 'sphere_adm_id')
+	    .field('indicadores_financeiros.dados_financeiros', 'financial_data_id')
+	    .group('indicadores_financeiros.ano_censo')
+	    .group('indicadores_financeiros.estado_id')
+	    .group('estado.sigla')
+	    .group('indicadores_financeiros.valor')
+	    .group('indicadores_financeiros.dados_financeiros')
+	    .group('indicadores_financeiros.esfera_adm')
+		} else {
+	    req.sql.from('indicadores_financeiros')
+	    .field('indicadores_financeiros.estado_id', 'state_id')
+	    .field('indicadores_financeiros.ano_censo', 'year')
+	    .field('estado.sigla', 'state_abbreviation')
+	    .field('indicadores_financeiros.valor', 'valor')
+	    .field('indicadores_financeiros.esfera_adm', 'sphere_adm_id')
+	    .field('indicadores_financeiros.dados_financeiros', 'financial_data_id')
+	    .join('estado', null, 'indicadores_financeiros.estado_id=estado.id')
+	    .group('indicadores_financeiros.ano_censo')
+	    .group('indicadores_financeiros.estado_id')
+	    .group('estado.sigla')
+	    .group('indicadores_financeiros.valor')
+	    .group('indicadores_financeiros.dados_financeiros')
+	    .group('indicadores_financeiros.esfera_adm')
+		}
+    next();
+}, query, id2str.transform(), response('financial'));
+
+module.exports = financialApp;
diff --git a/src/libs/routes_v2/glossEnrollmentRatio.js b/src/libs/routes_v2/glossEnrollmentRatio.js
new file mode 100644
index 00000000..ab57e325
--- /dev/null
+++ b/src/libs/routes_v2/glossEnrollmentRatio.js
@@ -0,0 +1,390 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const glossEnrollmentRatioApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const multiQuery = require(`${libs}/middlewares/multiQuery`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+glossEnrollmentRatioApp.use(cache('15 day'));
+
+// Complete range of the enrollments dataset.
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+glossEnrollmentRatioApp.get('/year_range', (req, res, next) => {
+    req.sql.from('pnad')
+    .field('DISTINCT pnad.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('matricula')
+    .field('DISTINCT matricula.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let distinct_years = [];
+    let new_result = [];
+    for (let i = 0; i < req.oldResult.length; i++) {
+        for (let j = 0; j < req.result.length; j++) {
+            if(req.oldResult[i].year == req.result[j].year) {
+                distinct_years.push(req.oldResult[i]);
+            }
+        }
+    }
+    new_result.push({start_year: distinct_years[distinct_years.length -1].year, end_year: distinct_years[0].year});
+    req.result = new_result;
+    next();
+}, response('range'));
+
+glossEnrollmentRatioApp.get('/years', (req, res, next) => {
+    req.sql.from('pnad')
+    .field('DISTINCT pnad.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('matricula')
+    .field('DISTINCT matricula.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let new_result = []
+    for (let i = 0; i < req.oldResult.length; i++) {
+        for (let j = 0; j < req.result.length; j++) {
+            if(req.oldResult[i].year == req.result[j].year) {
+                new_result.push(req.oldResult[i]);
+            }
+        }
+    }
+    req.result = new_result;
+    next();
+}, response('years'));
+
+glossEnrollmentRatioApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'pnad\'');
+    next();
+}, query, response('source'));
+
+glossEnrollmentRatioApp.get('/education_level_short', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não classificada'},
+        {id: 1, name: 'Creche'},
+        {id: 2, name: 'Pré-Escola'},
+        {id: 3, name: 'Ensino Fundamental - anos iniciais'},
+        {id: 4, name: 'Ensino Fundamental - anos finais'},
+        {id: 5, name: 'Ensino Médio'}
+    ];
+    next();
+}, response('education_level_short'));
+
+glossEnrollmentRatioApp.get('/gender', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Masculino'},
+        {id: 2, name: 'Feminino'}
+    ];
+    next();
+}, response('gender'));
+
+glossEnrollmentRatioApp.get('/ethnic_group', (req, res, next) => {
+    req.result = [
+        {id: 0, name: 'Sem declaração'},
+        {id: 1, name: 'Branca'},
+        {id: 2, name: 'Preta'},
+        {id: 3, name: 'Parda'},
+        {id: 4, name: 'Amarela'},
+        {id: 5, name: 'Indígena'}
+    ];
+    next();
+}, response('ethnic_group'));
+
+glossEnrollmentRatioApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'ethnic_group',
+    table: '@',
+    tableField: 'cor_raca_id',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca_id'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'gender',
+    table: '@',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'location',
+    table: '@',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'education_level_short',
+    table: 'matricula',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+});
+
+function matchQueries(queryTotal, queryPartial) {
+    let match = [];
+    queryPartial.forEach((result) => {
+        let newObj = {};
+        let keys = Object.keys(result);
+        keys.forEach((key) => {
+            newObj[key] = result[key];
+        });
+        // console.log('NEW OBJ');
+        // console.log(newObj);
+        // remove total
+        let index = keys.indexOf('total');
+        if(index > -1) keys.splice(index, 1);
+        // remove education_level_short_id
+        index = keys.indexOf('education_level_short_id');
+        if(index > -1) keys.splice(index, 1);
+        // remove education_level_short_name
+        index = keys.indexOf('education_level_short_name');
+        if(index > -1) keys.splice(index, 1);
+        let objMatch = null;
+
+        for(let i = 0; i < queryTotal.length; ++i) {
+            let total = queryTotal[i];
+            let foundMatch = true;
+            for(let j = 0; j < keys.length; ++j) {
+                let key = keys[j];
+                if(total[key] !== result[key]) {
+                    foundMatch = false;
+                    break;
+                }
+            }
+            if(foundMatch) {
+                objMatch = total;
+                break;
+            }
+        }
+
+        if(objMatch) {
+            // console.log('MATCH!!!!');
+            // console.log(objMatch);
+            newObj.total = (result.total / objMatch.total) * 100;
+            newObj.partial = result.total;
+            newObj.denominator = objMatch.total
+            match.push(newObj);
+        }
+    });
+    // console.log('TAMANHOS');
+    // console.log(queryTotal.length);
+    // console.log(queryPartial.length);
+    // console.log(match.length);
+    return match;
+}
+
+glossEnrollmentRatioApp.get('/', rqf.parse(),(req, res, next) => {
+    req.numerator = {};
+    req.denominator = {};
+    let glossEnrollmentRatioApp = {};
+
+    req.sql.from('matricula')
+    .field('count(*)', 'total')
+    .field('matricula.ano_censo', 'year')
+    .group('matricula.ano_censo')
+    .order('matricula.ano_censo')
+    .where('matricula.tipo <= 3')
+
+    if ( "education_level_short" in req.dims ) {
+        req.sql.field('matricula.etapa_resumida', 'age_range')
+        req.sql.where('matricula.etapa_resumida = 1 OR matricula.etapa_resumida = 2 OR matricula.etapa_resumida = 3 OR matricula.etapa_resumida = 4 OR matricula.etapa_resumida = 5')
+        req.sql.group('matricula.etapa_resumida', 'age_range');
+    }
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    req.numerator = req.result;
+    req.resetSql();
+    req.sql.field('sum(peso)', 'total')
+    .field('pnad.ano_censo','year')
+    .from('pnad')
+    .group('pnad.ano_censo')
+    .order('pnad.ano_censo')
+
+    function convert(result) {
+        if (result == 1) {
+            return 'pnad.faixa_etaria_31_03 = 1'
+        } else if (result == 2) {
+            return 'pnad.faixa_etaria_31_03 = 2'
+        } else if (result == 4) {
+            return 'pnad.faixa_etaria_31_03 = 3'
+        } else if (result == 5) {
+            return 'pnad.faixa_etaria_31_03 = 4'
+        } else if (result == 6) {
+            return 'pnad.faixa_etaria_31_03 = 5'
+        }
+    }
+
+    //remove education_level_short how filter and add faixa_etaria_31_03 in filter
+    if ("education_level_short" in req.filter) {
+        if (Array.isArray(req.filter.education_level_short)) {
+            var string_query = '';
+            for(let i = 0; i < req.filter.education_level_short.length - 1; i++)  {
+                string_query = string_query + convert(req.filter.education_level_short[i]) + ' OR ';
+            }
+            string_query = string_query + convert(req.filter.education_level_short[req.filter.education_level_short.length - 1]);
+            req.sql.where(string_query);
+            req.sql.field('pnad.faixa_etaria_31_03','age_range')
+            req.sql.group('pnad.faixa_etaria_31_03', 'age_range')
+        }
+    } else if ( "education_level_short" in req.dims ) {
+        req.sql.field('pnad.faixa_etaria_31_03','age_range')
+        req.sql.where('pnad.faixa_etaria_31_03 = 1 OR pnad.faixa_etaria_31_03 = 2 OR pnad.faixa_etaria_31_03 = 3 OR pnad.faixa_etaria_31_03 = 4 OR pnad.faixa_etaria_31_03 = 5')
+        req.sql.group('pnad.faixa_etaria_31_03', 'age_range');
+    } else {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Wrong/No filter specified'
+        });
+    }
+
+    next();
+}, rqf.parse(), (req, res, next) => {
+    if ("education_level_short" in req.filter) {
+        delete req.filter.education_level_short;
+    }
+    if ("education_level_short" in req.dims) {
+        delete req.dims.education_level_short;
+    }
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    req.denominator = req.result;
+    req.result = []
+    let glossEnrollment = matchQueries(req.denominator, req.numerator);
+    req.result = glossEnrollment;
+
+    next();
+}, response('glossEnrollmentRatio'));
+
+module.exports = glossEnrollmentRatioApp;
diff --git a/src/libs/routes_v2/idhm.js b/src/libs/routes_v2/idhm.js
new file mode 100644
index 00000000..5b941793
--- /dev/null
+++ b/src/libs/routes_v2/idhm.js
@@ -0,0 +1,212 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const idhmApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+idhmApp.use(cache('15 day'));
+
+idhmApp.get('/year_range', (req, res, next) => {
+    req.sql.from('adh_idh')
+    .field('MIN(adh_idh.ano_censo)', 'start_year')
+    .field('MAX(adh_idh.ano_censo)', 'end_year');
+    next();
+}, query, (req, res, next) => {
+    req.sql.from('adh_idh_uf')
+    .field('MIN(adh_idh_uf.ano_censo)', 'start_year')
+    .field('MAX(adh_idh_uf.ano_censo)', 'end_year');
+    req.old_result = req.result;
+    next();
+}, query, (req, res, next) => {
+    if (req.old_result[0].start_year < req.result[0].start_year) {
+        req.result[0].start_year = req.old_result[0].start_year;
+    }
+    if (req.old_result[0].end_year > req.result[0].end_year) {
+        req.result[0].end_year = req.old_result[0].old_result;
+    }
+    next();
+}, query, response('range'));
+
+idhmApp.get('/years', (req, res, next) => {
+    req.sql.from('adh_idh')
+    .field('DISTINCT adh_idh.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('adh_idh_uf')
+    .field('DISTINCT adh_idh_uf.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let result = Object.assign(req.oldResult, req.result);
+    req.result = result;
+    next();
+}, response('years'));
+
+idhmApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'adh_idh\'');
+    next();
+}, query, response('source'));
+
+idhmApp.get('/IDHM_level', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não classificada'},
+        {id: 1, name: 'Muito Baixa'},
+        {id: 2, name: 'Baixo'},
+        {id: 3, name: 'Médio'},
+        {id: 4, name: 'Alto'},
+        {id: 5, name: 'Muito Alto'}
+    ];
+    next();
+}, response('IDHM_level'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'adh_idh'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'adh_idh'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'idhm_level',
+    table: '@',
+    tableField: 'idhm_nivel',
+    resultField: 'idhm_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: '@',
+        field: 'idhm_nivel'
+    }
+});
+
+
+idhmApp.get('/', rqf.parse(), (req, res, next) => {
+      if (("city" in req.dims) || ("city" in req.filter)) {
+          req.sql.from('adh_idh')
+          .field('adh_idh.idhm', 'total')
+          .field('adh_idh.ano_censo', 'year')
+          .field('adh_idh.municipio_id', 'city_id')
+          .field('adh_idh.estado_id', 'state_id')
+          .group('adh_idh.idhm')
+          .group('adh_idh.ano_censo')
+          .group('adh_idh.municipio_id')
+          .group('adh_idh.estado_id')
+      } else if (("state" in req.filter) || ("state" in req.dims)) {
+          req.sql.from('adh_idh_uf')
+          .field('adh_idh_uf.idhm', 'total')
+          .field('adh_idh_uf.ano_censo', 'year')
+          .field('adh_idh_uf.estado_id', 'state_id')
+          .group('adh_idh_uf.idhm')
+          .group('adh_idh_uf.ano_censo')
+          .group('adh_idh_uf.estado_id')
+        } else {
+            res.status(400);
+            next({
+                status: 400,
+                message: 'Wrong/No filter specified'
+            });
+        }
+        next();
+}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('idhm'));
+
+module.exports = idhmApp;
diff --git a/src/libs/routes_v2/idhme.js b/src/libs/routes_v2/idhme.js
new file mode 100644
index 00000000..1b492c61
--- /dev/null
+++ b/src/libs/routes_v2/idhme.js
@@ -0,0 +1,185 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const idhmeApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+idhmeApp.use(cache('15 day'));
+
+idhmeApp.get('/year_range', (req, res, next) => {
+    req.sql.from('adh_idh')
+    .field('MIN(adh_idh.ano_censo)', 'start_year')
+    .field('MAX(adh_idh.ano_censo)', 'end_year');
+    next();
+}, query, (req, res, next) => {
+    req.sql.from('adh_idh_uf')
+    .field('MIN(adh_idh_uf.ano_censo)', 'start_year')
+    .field('MAX(adh_idh_uf.ano_censo)', 'end_year');
+    req.old_result = req.result;
+    next();
+}, query, (req, res, next) => {
+    if (req.old_result[0].start_year < req.result[0].start_year) {
+        req.result[0].start_year = req.old_result[0].start_year;
+    }
+    if (req.old_result[0].end_year > req.result[0].end_year) {
+        req.result[0].end_year = req.old_result[0].old_result;
+    }
+    next();
+}, query, response('range'));
+
+idhmeApp.get('/years', (req, res, next) => {
+    req.sql.from('adh_idh')
+    .field('DISTINCT adh_idh.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('adh_idh_uf')
+    .field('DISTINCT adh_idh_uf.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let result = Object.assign(req.oldResult, req.result);
+    req.result = result;
+    next();
+}, response('years'));
+
+idhmeApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'adh_idh\'');
+    next();
+}, query, response('source'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'adh_idh'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'adh_idh'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+});
+
+idhmeApp.get('/', rqf.parse(), (req, res, next) => {
+
+    if (("city" in req.dims) || ("city" in req.filter)) {
+        req.sql.from('adh_idh')
+        .field('adh_idh.idhm_e', 'total')
+        .field('adh_idh.ano_censo', 'year')
+        .field('adh_idh.municipio_id', 'city_id')
+        .group('adh_idh.idhm_e')
+        .group('adh_idh.ano_censo')
+        .group('adh_idh.municipio_id');
+    } else if (("state" in req.filter) || ("state" in req.dims)) {
+        req.sql.from('adh_idh_uf')
+        .field('adh_idh_uf.idhm_e', 'total')
+        .field('adh_idh_uf.ano_censo', 'year')
+        .field('adh_idh_uf.estado_id', 'state_id')
+        .group('adh_idh_uf.idhm_e')
+        .group('adh_idh_uf.ano_censo')
+        .group('adh_idh_uf.estado_id');
+    } else {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Wrong/No filter specified'
+        });
+    }
+    next();
+}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('idhme'));
+
+module.exports = idhmeApp;
diff --git a/src/libs/routes_v2/idhml.js b/src/libs/routes_v2/idhml.js
new file mode 100644
index 00000000..eee89ddf
--- /dev/null
+++ b/src/libs/routes_v2/idhml.js
@@ -0,0 +1,185 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const idhmlApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+idhmlApp.use(cache('15 day'));
+
+idhmlApp.get('/year_range', (req, res, next) => {
+    req.sql.from('adh_idh')
+    .field('MIN(adh_idh.ano_censo)', 'start_year')
+    .field('MAX(adh_idh.ano_censo)', 'end_year');
+    next();
+}, query, (req, res, next) => {
+    req.sql.from('adh_idh_uf')
+    .field('MIN(adh_idh_uf.ano_censo)', 'start_year')
+    .field('MAX(adh_idh_uf.ano_censo)', 'end_year');
+    req.old_result = req.result;
+    next();
+}, query, (req, res, next) => {
+    if (req.old_result[0].start_year < req.result[0].start_year) {
+        req.result[0].start_year = req.old_result[0].start_year;
+    }
+    if (req.old_result[0].end_year > req.result[0].end_year) {
+        req.result[0].end_year = req.old_result[0].old_result;
+    }
+    next();
+}, query, response('range'));
+
+idhmlApp.get('/years', (req, res, next) => {
+    req.sql.from('adh_idh')
+    .field('DISTINCT adh_idh.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('adh_idh_uf')
+    .field('DISTINCT adh_idh_uf.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let result = Object.assign(req.oldResult, req.result);
+    req.result = result;
+    next();
+}, response('years'));
+
+idhmlApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'adh_idh\'');
+    next();
+}, query, response('source'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'adh_idh'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'adh_idh'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+});
+
+idhmlApp.get('/', rqf.parse(), (req, res, next) => {
+
+    if (("city" in req.dims) || ("city" in req.filter)) {
+        req.sql.from('adh_idh')
+        .field('adh_idh.idhm_l', 'total')
+        .field('adh_idh.ano_censo', 'year')
+        .field('adh_idh.municipio_id', 'city_id')
+        .group('adh_idh.idhm_l')
+        .group('adh_idh.ano_censo')
+        .group('adh_idh.municipio_id');
+    } else if (("state" in req.filter) || ("state" in req.dims)) {
+        req.sql.from('adh_idh_uf')
+        .field('adh_idh_uf.idhm_l', 'total')
+        .field('adh_idh_uf.ano_censo', 'year')
+        .field('adh_idh_uf.estado_id', 'state_id')
+        .group('adh_idh_uf.idhm_l')
+        .group('adh_idh_uf.ano_censo')
+        .group('adh_idh_uf.estado_id');
+    } else {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Wrong/No filter specified'
+        });
+    }
+    next();
+}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('idhme'));
+
+module.exports = idhmlApp;
diff --git a/src/libs/routes_v2/idhmr.js b/src/libs/routes_v2/idhmr.js
new file mode 100644
index 00000000..a9117cb4
--- /dev/null
+++ b/src/libs/routes_v2/idhmr.js
@@ -0,0 +1,188 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const idhmrApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+idhmrApp.use(cache('15 day'));
+
+idhmrApp.get('/year_range', (req, res, next) => {
+    req.sql.from('adh_idh')
+    .field('MIN(adh_idh.ano_censo)', 'start_year')
+    .field('MAX(adh_idh.ano_censo)', 'end_year');
+    next();
+}, query, (req, res, next) => {
+    req.sql.from('adh_idh_uf')
+    .field('MIN(adh_idh_uf.ano_censo)', 'start_year')
+    .field('MAX(adh_idh_uf.ano_censo)', 'end_year');
+    req.old_result = req.result;
+    next();
+}, query, (req, res, next) => {
+    if (req.old_result[0].start_year < req.result[0].start_year) {
+        req.result[0].start_year = req.old_result[0].start_year;
+    }
+    if (req.old_result[0].end_year > req.result[0].end_year) {
+        req.result[0].end_year = req.old_result[0].old_result;
+    }
+    next();
+}, query, response('range'));
+
+idhmrApp.get('/years', (req, res, next) => {
+    req.sql.from('adh_idh')
+    .field('DISTINCT adh_idh.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('adh_idh_uf')
+    .field('DISTINCT adh_idh_uf.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let result = Object.assign(req.oldResult, req.result);
+    req.result = result;
+    next();
+}, response('years'));
+
+idhmrApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'adh_idh\'');
+    next();
+}, query, response('source'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'adh_idh'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'adh_idh'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+});
+
+idhmrApp.get('/', rqf.parse(), (req, res, next) => {
+    if (("city" in req.dims) || ("city" in req.filter)) {
+        req.sql.from('adh_idh')
+        .field('adh_idh.idhm_r', 'total')
+        .field('adh_idh.ano_censo', 'year')
+        .field('adh_idh.municipio_id', 'city_id')
+        .field('adh_idh.estado_id', 'state_id')
+        .group('adh_idh.idhm_r')
+        .group('adh_idh.ano_censo')
+        .group('adh_idh.municipio_id')
+        .group('adh_idh.estado_id')
+    } else if (("state" in req.filter) || ("state" in req.dims)) {
+        req.sql.from('adh_idh_uf')
+        .field('adh_idh_uf.idhm_r', 'total')
+        .field('adh_idh_uf.ano_censo', 'year')
+        .field('adh_idh_uf.estado_id', 'state_id')
+        .group('adh_idh_uf.idhm_r')
+        .group('adh_idh_uf.ano_censo')
+        .group('adh_idh_uf.estado_id')
+      } else {
+          res.status(400);
+          next({
+              status: 400,
+              message: 'Wrong/No filter specified'
+          });
+      }
+      next();
+}, rqf.build(), query, addMissing(rqf), id2str.transform(), response('idhmr'));
+
+module.exports = idhmrApp;
diff --git a/src/libs/routes_v2/infrastructure.js b/src/libs/routes_v2/infrastructure.js
new file mode 100644
index 00000000..1ececa91
--- /dev/null
+++ b/src/libs/routes_v2/infrastructure.js
@@ -0,0 +1,585 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const infrastructureApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const multiQuery = require(`${libs}/middlewares/multiQuery`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+infrastructureApp.use(cache('15 day'));
+
+infrastructureApp.get('/year_range', (req, res, next) => {
+    req.sql.from('escola')
+    .field('MIN(escola.ano_censo)', 'start_year')
+    .field('MAX(escola.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+infrastructureApp.get('/years', (req, res, next) => {
+    req.sql.from('escola')
+    .field('DISTINCT escola.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+infrastructureApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'escola\'');
+    next();
+}, query, response('source'));
+
+infrastructureApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+infrastructureApp.get('/rural_location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: "Urbana"},
+        {id: 2, name: "Rural"},
+        {id: 3, name: "Rural - Área de assentamento"},
+        {id: 4, name: "Rural - Terra indígena"},
+        {id: 5, name: "Rural - Área remanescente de quilombos"},
+        {id: 6, name: "Rural - Unidade de uso sustentável"}
+    ];
+    next();
+}, response('rural_location'));
+
+infrastructureApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+infrastructureApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'id',
+    resultField: 'city_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}, 'filter').addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'escola'
+    }
+}, 'dims').addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: 'id',
+    resultField: 'state_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'escola'
+    }
+}, 'filter').addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'location',
+    table: 'escola',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'escola',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'escola',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'escola',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+});
+
+function matchQueries(queryTotal, queryPartial) {
+    let match = [];
+    queryTotal.forEach((result) => {
+        let newObj = {};
+        let keys = Object.keys(result);
+        keys.forEach((key) => {
+            newObj[key] = result[key];
+        });
+        let index = keys.indexOf('total');
+        if(index > -1) keys.splice(index, 1);
+        let objMatch = null;
+
+        for(let i = 0; i < queryPartial.length; ++i) {
+            let partial = queryPartial[i];
+            let foundMatch = true;
+            for(let j = 0; j < keys.length; ++j) {
+                let key = keys[j];
+                if(partial[key] !== result[key]) {
+                    foundMatch = false;
+                    break;
+                }
+            }
+            if(foundMatch) {
+                objMatch = partial;
+                break;
+            }
+        }
+
+        if(objMatch) {
+            newObj.percentage = (objMatch.total / result.total) * 100;
+            newObj.partial = objMatch.total;
+            newObj.total = result.total
+            match.push(newObj);
+        }
+    });
+
+    return match;
+}
+
+infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.querySet = [];
+    req.queryIndex = {};
+
+    // Local de funcionamento
+    let allSchools = req.sql.clone();
+    allSchools.from('escola').field('COUNT(escola.id)', 'total')
+    .field("'Brasil'", 'name')
+    .field('escola.ano_censo', 'year')
+    .group('escola.ano_censo')
+    .where('escola.situacao_de_funcionamento = 1')
+    .order('escola.ano_censo');
+    req.queryIndex.allSchools = req.querySet.push(allSchools) - 1;
+
+    let schoolPlace = allSchools.clone();
+    schoolPlace.where('escola.func_predio_escolar = 1 AND escola.func_salas_empresa = 0 AND escola.func_templo_igreja = 0 AND escola.func_casa_professor = 0 AND escola.func_galpao = 0 AND escola.biblioteca = 1');
+    req.queryIndex.schoolPlace = req.querySet.push(schoolPlace) - 1;
+
+    // Bibliotecas
+    let allLibraries = allSchools.clone();
+    allLibraries.where('escola.func_predio_escolar = 1 AND escola.localizacao_id = 1');
+    req.queryIndex.allLibraries = req.querySet.push(allLibraries) - 1;
+
+    let haveLibraries = allLibraries.clone();
+    haveLibraries.where('escola.biblioteca = 1');
+    req.queryIndex.haveLibraries = req.querySet.push(haveLibraries) - 1;
+
+    // Bibliotecas/Sala de leitura
+    let allLibrariesReadingRoom = allSchools.clone();
+    allLibrariesReadingRoom.where('escola.func_predio_escolar = 1 AND escola.localizacao_id = 2');
+    req.queryIndex.allLibrariesReadingRoom = req.querySet.push(allLibrariesReadingRoom) - 1;
+
+    let haveLibrariesReadingRoom = allLibrariesReadingRoom.clone();
+    haveLibrariesReadingRoom.where('escola.sala_leitura = 1');
+    req.queryIndex.haveLibrariesReadingRoom = req.querySet.push(haveLibrariesReadingRoom) - 1;
+
+    // Laboratório de informática
+    let allInfLab = allSchools.clone();
+    allInfLab.where('escola.func_predio_escolar = 1')
+    .where('escola.reg_fund_ai = 1 OR escola.reg_fund_af = 1 OR escola.reg_medio_medio = 1 OR escola.reg_medio_integrado = 1 OR escola.reg_medio_normal = 1 OR escola.ensino_eja_fund = 1 OR escola.ensino_eja_medio = 1 OR escola.ensino_eja_prof = 1');
+    req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1;
+
+    let haveInfLab = allInfLab.clone();
+    haveInfLab.where('escola.lab_informatica = 1');
+    req.queryIndex.haveInfLab = req.querySet.push(haveInfLab) - 1;
+
+    // Laboratório de ciências
+    let allScienceLab = allInfLab.clone();
+    req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1;
+
+    let haveScienceLab = allScienceLab.clone();
+    haveScienceLab.where('escola.lab_ciencias = 1');
+    req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1;
+
+    // Parque infantil
+    let allKidsPark = allSchools.clone();
+    allKidsPark.where('escola.func_predio_escolar = 1')
+    .where('escola.reg_infantil_creche = 1 OR escola.reg_infantil_preescola = 1 OR escola.reg_fund_ai = 1 OR escola.esp_infantil_creche = 1 OR escola.esp_exclusiva_creche = 1 OR escola.reg_esp_exclusiva_fund_ai = 1');
+    req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1;
+
+    let haveKidsPark = allKidsPark.clone();
+    haveKidsPark.where('escola.parque_infantil = 1');
+    req.queryIndex.haveKidsPark = req.querySet.push(haveKidsPark) - 1;
+
+    // Berçário
+    let allCribs = allSchools.clone();
+    allCribs.where('escola.func_predio_escolar = 1')
+    .where('escola.reg_infantil_creche = 1 OR escola.esp_infantil_creche = 1');
+    req.queryIndex.allCribs = req.querySet.push(allCribs) - 1;
+
+    let haveCribs = allCribs.clone();
+    haveCribs.where('escola.bercario = 1');
+    req.queryIndex.haveCribs = req.querySet.push(haveCribs) - 1;
+
+    // Quadra
+    let allSportsCourt = allScienceLab.clone();
+    allSportsCourt.where('escola.localizacao_id = 1');
+    req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1;
+
+    let haveSportsCourt = allSportsCourt.clone();
+    haveSportsCourt.where('escola.quadra_esportes = 1');
+    req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1;
+
+    // Quadra coberta
+    req.queryIndex.allCoveredSportsCourt = req.queryIndex.allSportsCourt;
+
+    let haveCoveredSportsCourt = allSportsCourt.clone();
+    haveCoveredSportsCourt.where('escola.quadra_esportes_coberta = 1');
+    req.queryIndex.haveCoveredSportsCourt = req.querySet.push(haveCoveredSportsCourt) - 1;
+
+    // Quadra Descoberta
+    let allUncoveredSportsCourt = allSportsCourt.clone();
+    allUncoveredSportsCourt.where('escola.quadra_esportes_coberta = 0');
+    req.queryIndex.allUncoveredSportsCourt = req.querySet.push(allUncoveredSportsCourt) - 1;
+
+    let haveUncoveredSportsCourt = allUncoveredSportsCourt.clone();
+    haveUncoveredSportsCourt.where('escola.quadra_esportes_descoberta = 1');
+    req.queryIndex.haveUncoveredSportsCourt = req.querySet.push(haveUncoveredSportsCourt) - 1;
+
+    // Sala de direção
+    let allDirectorRoom = allSchools.clone();
+    allDirectorRoom.where('escola.func_predio_escolar = 1 AND escola.localizacao_id = 1');
+    req.queryIndex.allDirectorRoom = req.querySet.push(allDirectorRoom) - 1;
+
+    let haveDirectorRoom = allDirectorRoom.clone();
+    haveDirectorRoom.where('escola.sala_diretoria = 1');
+    req.queryIndex.haveDirectorRoom = req.querySet.push(haveDirectorRoom) - 1;
+
+    // Secretaria
+    let allSecretary = allSchools.clone();
+    allSecretary.where('escola.func_predio_escolar = 1');
+    req.queryIndex.allSecretary = req.querySet.push(allSecretary) - 1;
+
+    let haveSecretary = allSecretary.clone();
+    haveSecretary.where('escola.secretaria = 1');
+    req.queryIndex.haveSecretary = req.querySet.push(haveSecretary) - 1;
+
+    // Sala de professores
+    req.queryIndex.allTeacherRoom = req.queryIndex.allSecretary;
+
+    let haveTeacherRoom = allSecretary.clone();
+    haveTeacherRoom.where('escola.sala_professor = 1');
+    req.queryIndex.haveTeacherRoom = req.querySet.push(haveTeacherRoom) - 1;
+
+    // Cozinha
+    req.queryIndex.allKitchen = req.queryIndex.allSecretary;
+
+    let haveKitchen = allSecretary.clone();
+    haveKitchen.where('escola.cozinha = 1');
+    req.queryIndex.haveKitchen = req.querySet.push(haveKitchen) - 1;
+
+    // Despensa
+    req.queryIndex.allStoreroom = req.queryIndex.allSecretary;
+
+    let haveStoreroom = allSecretary.clone();
+    haveStoreroom.where('escola.despensa = 1');
+    req.queryIndex.haveStoreroom = req.querySet.push(haveStoreroom) - 1;
+
+    // Almoxarifado
+    req.queryIndex.allWarehouse = req.queryIndex.allSecretary;
+
+    let haveWarehouse = allSecretary.clone();
+    haveWarehouse.where('escola.almoxarifado = 1');
+    req.queryIndex.haveWarehouse = req.querySet.push(haveWarehouse) - 1;
+
+    // Internet
+    req.queryIndex.allInternet = req.queryIndex.allLibrariesReadingRoom;
+
+    let haveInternet = allLibrariesReadingRoom.clone();
+    haveInternet.where('escola.internet = 1');
+    req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1;
+
+    // Internet banda larga
+    req.queryIndex.allBroadbandInternet = req.queryIndex.allLibraries;
+
+    let haveBroadbandInternet = allLibraries.clone();
+    haveBroadbandInternet.where('escola.internet_banda_larga = 1');
+    req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1;
+
+    // Banheiro dentro do prédio
+    req.queryIndex.allInsideBathroom = req.queryIndex.allSecretary;
+
+    let haveInsideBathroom = allSecretary.clone();
+    haveInsideBathroom.where('escola.sanitario_dentro_predio = 1');
+    req.queryIndex.haveInsideBathroom = req.querySet.push(haveInsideBathroom) - 1;
+
+    // Banheiro adequado para educação infantil dentro do prédio
+    req.queryIndex.allInsideKidsBathroom = req.queryIndex.allKidsPark;
+
+    let haveInsideKidsBathroom = allKidsPark.clone();
+    haveInsideKidsBathroom.where('escola.sanitario_ei = 1');
+    req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1;
+
+    // Fornecimento de energia
+    req.queryIndex.allEletricEnergy = req.queryIndex.allSecretary;
+
+    let haveEletricEnergy = allSecretary.clone();
+    haveEletricEnergy.where('escola.fornecimento_energia = 1');
+    req.queryIndex.haveEletricEnergy = req.querySet.push(haveEletricEnergy) - 1;
+
+    // Abastecimento de água
+    req.queryIndex.allWaterSupply = req.queryIndex.allSecretary;
+
+    let haveWaterSupply = allSecretary.clone();
+    haveWaterSupply.where('escola.fornecimento_agua = 1');
+    req.queryIndex.haveWaterSupply = req.querySet.push(haveWaterSupply) - 1;
+
+    // Água filtrada
+    req.queryIndex.allFilteredWater = req.queryIndex.allSecretary;
+
+    let haveFilteredWater = allSecretary.clone();
+    haveFilteredWater.where('escola.agua_filtrada = 1');
+    req.queryIndex.haveFilteredWater = req.querySet.push(haveFilteredWater) - 1;
+
+    // Coleta de esgoto
+    req.queryIndex.allSewage = req.queryIndex.allSecretary;
+
+    let haveSewage = allSecretary.clone();
+    haveSewage.where('escola.esgoto_sanitario = 1');
+    req.queryIndex.haveSewage = req.querySet.push(haveSewage) - 1;
+
+    // Sala de recursos multifuncionais para Atendimento Educacional Especializado
+    req.queryIndex.allMultifunctionRoom = req.queryIndex.allSecretary;
+
+    let haveMultifunctionRoom = allSecretary.clone();
+    haveMultifunctionRoom.where('escola.sala_atendimento_especial = 1');
+    req.queryIndex.haveMultifunctionRoom = req.querySet.push(haveMultifunctionRoom) - 1;
+
+    // Banheiros adaptados para pessoas com deficiências
+    req.queryIndex.allSpecialBathroom = req.queryIndex.allSecretary;
+
+    let haveSpecialBathroom = allSecretary.clone();
+    haveSpecialBathroom.where('escola.sanitario_pne = 1');
+    req.queryIndex.haveSpecialBathroom = req.querySet.push(haveSpecialBathroom) - 1;
+
+    // Dependências adaptada para pessoas com deficiências
+    req.queryIndex.allAdaptedBuilding = req.queryIndex.allSecretary;
+
+    let haveAdaptedBuilding = allSecretary.clone();
+    haveAdaptedBuilding.where('escola.dependencias_pne = 1');
+    req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1;
+
+    next();
+}, multiQuery, (req, res, next) => {
+    // Faz o matching entre os resultados
+    let school_place = matchQueries(req.result[req.queryIndex.allSchools], req.result[req.queryIndex.schoolPlace]);
+    let libraries = matchQueries(req.result[req.queryIndex.allLibraries], req.result[req.queryIndex.haveLibraries]);
+    let libraries_reading_room = matchQueries(req.result[req.queryIndex.allLibrariesReadingRoom], req.result[req.queryIndex.haveLibrariesReadingRoom]);
+    let computer_lab = matchQueries(req.result[req.queryIndex.allInfLab], req.result[req.queryIndex.haveInfLab]);
+    let science_lab = matchQueries(req.result[req.queryIndex.allScienceLab], req.result[req.queryIndex.haveScienceLab]);
+    let kids_park = matchQueries(req.result[req.queryIndex.allKidsPark], req.result[req.queryIndex.haveKidsPark]);
+    let nursery = matchQueries(req.result[req.queryIndex.allCribs], req.result[req.queryIndex.haveCribs]);
+    let sports_court = matchQueries(req.result[req.queryIndex.allSportsCourt], req.result[req.queryIndex.haveSportsCourt]);
+    let covered_sports_court = matchQueries(req.result[req.queryIndex.allCoveredSportsCourt], req.result[req.queryIndex.haveCoveredSportsCourt]);
+    let uncovered_sports_court = matchQueries(req.result[req.queryIndex.allUncoveredSportsCourt], req.result[req.queryIndex.haveUncoveredSportsCourt]);
+    let director_room = matchQueries(req.result[req.queryIndex.allDirectorRoom], req.result[req.queryIndex.haveDirectorRoom]);
+    let secretary = matchQueries(req.result[req.queryIndex.allSecretary], req.result[req.queryIndex.haveSecretary]);
+    let teacher_room = matchQueries(req.result[req.queryIndex.allTeacherRoom], req.result[req.queryIndex.haveTeacherRoom]);
+    let kitchen = matchQueries(req.result[req.queryIndex.allKitchen], req.result[req.queryIndex.haveKitchen]);
+    let storeroom = matchQueries(req.result[req.queryIndex.allStoreroom], req.result[req.queryIndex.haveStoreroom]);
+    let warehouse = matchQueries(req.result[req.queryIndex.allWarehouse], req.result[req.queryIndex.haveWarehouse]);
+    let internet = matchQueries(req.result[req.queryIndex.allInternet], req.result[req.queryIndex.haveInternet]);
+    let broadband_internet = matchQueries(req.result[req.queryIndex.allBroadbandInternet], req.result[req.queryIndex.haveBroadbandInternet]);
+    let inside_bathroom = matchQueries(req.result[req.queryIndex.allInsideBathroom], req.result[req.queryIndex.haveInsideBathroom]);
+    let inside_kids_bathroom = matchQueries(req.result[req.queryIndex.allInsideKidsBathroom], req.result[req.queryIndex.haveInsideKidsBathroom]);
+    let eletric_energy = matchQueries(req.result[req.queryIndex.allEletricEnergy], req.result[req.queryIndex.haveEletricEnergy]);
+    let water_supply = matchQueries(req.result[req.queryIndex.allWaterSupply], req.result[req.queryIndex.haveWaterSupply]);
+    let filtered_water = matchQueries(req.result[req.queryIndex.allFilteredWater], req.result[req.queryIndex.haveFilteredWater]);
+    let sewage_treatment = matchQueries(req.result[req.queryIndex.allSewage], req.result[req.queryIndex.haveSewage]);
+    let special_multifunction_room = matchQueries(req.result[req.queryIndex.allMultifunctionRoom], req.result[req.queryIndex.haveMultifunctionRoom]);
+    let special_bathroom = matchQueries(req.result[req.queryIndex.allSpecialBathroom], req.result[req.queryIndex.haveSpecialBathroom]);
+    let adapted_building = matchQueries(req.result[req.queryIndex.allAdaptedBuilding], req.result[req.queryIndex.haveAdaptedBuilding]);
+
+    req.result = [{
+        school_place,
+        libraries,
+        libraries_reading_room,
+        computer_lab,
+        science_lab,
+        kids_park,
+        nursery,
+        sports_court,
+        covered_sports_court,
+        uncovered_sports_court,
+        director_room,
+        secretary,
+        teacher_room,
+        kitchen,
+        storeroom,
+        warehouse,
+        internet,
+        broadband_internet,
+        inside_bathroom,
+        inside_kids_bathroom,
+        eletric_energy,
+        water_supply,
+        filtered_water,
+        sewage_treatment,
+        special_multifunction_room,
+        special_bathroom,
+        adapted_building
+    }];
+
+    next();
+}, id2str.multitransform(false), response('infrastructure'));
+
+module.exports = infrastructureApp;
diff --git a/src/libs/routes_v2/liquidEnrollmentRatio.js b/src/libs/routes_v2/liquidEnrollmentRatio.js
new file mode 100644
index 00000000..63bd9168
--- /dev/null
+++ b/src/libs/routes_v2/liquidEnrollmentRatio.js
@@ -0,0 +1,434 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const liquidEnrollmentRatioApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const multiQuery = require(`${libs}/middlewares/multiQuery`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+liquidEnrollmentRatioApp.use(cache('15 day'));
+
+// Complete range of the enrollments dataset.
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+liquidEnrollmentRatioApp.get('/year_range', (req, res, next) => {
+    req.sql.from('pnad')
+    .field('DISTINCT pnad.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('matricula')
+    .field('DISTINCT matricula.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let distinct_years = [];
+    let new_result = [];
+    for (let i = 0; i < req.oldResult.length; i++) {
+        for (let j = 0; j < req.result.length; j++) {
+            if(req.oldResult[i].year == req.result[j].year) {
+                distinct_years.push(req.oldResult[i]);
+            }
+        }
+    }
+    new_result.push({start_year: distinct_years[distinct_years.length -1].year, end_year: distinct_years[0].year});
+    req.result = new_result;
+    next();
+}, response('range'));
+
+liquidEnrollmentRatioApp.get('/years', (req, res, next) => {
+    req.sql.from('pnad')
+    .field('DISTINCT pnad.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('matricula')
+    .field('DISTINCT matricula.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let new_result = []
+    for (let i = 0; i < req.oldResult.length; i++) {
+        for (let j = 0; j < req.result.length; j++) {
+            if(req.oldResult[i].year == req.result[j].year) {
+                new_result.push(req.oldResult[i]);
+            }
+        }
+    }
+    req.result = new_result;
+    next();
+}, response('years'));
+
+liquidEnrollmentRatioApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'pnad\'');
+    next();
+}, query, response('source'));
+
+liquidEnrollmentRatioApp.get('/education_level_short', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não classificada'},
+        {id: 1, name: 'Creche'},
+        {id: 2, name: 'Pré-Escola'},
+        {id: 3, name: 'Ensino Fundamental - anos iniciais'},
+        {id: 4, name: 'Ensino Fundamental - anos finais'},
+        {id: 5, name: 'Ensino Médio'}
+    ];
+    next();
+}, response('education_level_short'));
+
+liquidEnrollmentRatioApp.get('/gender', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Masculino'},
+        {id: 2, name: 'Feminino'}
+    ];
+    next();
+}, response('gender'));
+
+liquidEnrollmentRatioApp.get('/ethnic_group', (req, res, next) => {
+    req.result = [
+        {id: 0, name: 'Sem declaração'},
+        {id: 1, name: 'Branca'},
+        {id: 2, name: 'Preta'},
+        {id: 3, name: 'Parda'},
+        {id: 4, name: 'Amarela'},
+        {id: 5, name: 'Indígena'}
+    ];
+    next();
+}, response('ethnic_group'));
+
+liquidEnrollmentRatioApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'ethnic_group',
+    table: '@',
+    tableField: 'cor_raca_id',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca_id'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'gender',
+    table: '@',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'location',
+    table: '@',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'education_level_short',
+    table: 'matricula',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+});
+
+function matchQueries(queryTotal, queryPartial) {
+    let match = [];
+    queryPartial.forEach((result) => {
+        let newObj = {};
+        let keys = Object.keys(result);
+        keys.forEach((key) => {
+            newObj[key] = result[key];
+        });
+        // console.log('NEW OBJ');
+        // console.log(newObj);
+        // remove total
+        let index = keys.indexOf('total');
+        if(index > -1) keys.splice(index, 1);
+        // remove education_level_short_id
+        index = keys.indexOf('education_level_short_id');
+        if(index > -1) keys.splice(index, 1);
+        // remove education_level_short_name
+        index = keys.indexOf('education_level_short_name');
+        if(index > -1) keys.splice(index, 1);
+        let objMatch = null;
+
+        for(let i = 0; i < queryTotal.length; ++i) {
+            let total = queryTotal[i];
+            let foundMatch = true;
+            for(let j = 0; j < keys.length; ++j) {
+                let key = keys[j];
+                if(total[key] !== result[key]) {
+                    foundMatch = false;
+                    break;
+                }
+            }
+            if(foundMatch) {
+                objMatch = total;
+                break;
+            }
+        }
+
+        if(objMatch) {
+            // console.log('MATCH!!!!');
+            // console.log(objMatch);
+            newObj.total = (result.total / objMatch.total) * 100;
+            newObj.partial = result.total;
+            newObj.denominator = objMatch.total
+            match.push(newObj);
+        }
+    });
+    // console.log('TAMANHOS');
+    // console.log(queryTotal.length);
+    // console.log(queryPartial.length);
+    // console.log(match.length);
+    return match;
+}
+
+function ConvertEnrollment(result) {
+    if (result == 1) {
+        return '(matricula.faixa_etaria_31_03 = 1 AND matricula.etapa_resumida = 1)'
+    } else if (result == 2) {
+        return '(matricula.faixa_etaria_31_03 = 2 AND matricula.etapa_resumida = 2)'
+    } else if (result == 4) {
+        return '(matricula.faixa_etaria_31_03 = 3 AND matricula.etapa_resumida = 3)'
+    } else if (result == 5) {
+        return '(matricula.faixa_etaria_31_03 = 4 AND matricula.etapa_resumida = 4)'
+    } else if (result == 6) {
+        return '(matricula.faixa_etaria_31_03 = 5 AND matricula.etapa_resumida = 5)'
+    }
+}
+
+
+function convertPnad(result) {
+    if (result == 1) {
+        return 'pnad.faixa_etaria_31_03 = 1'
+    } else if (result == 2) {
+        return 'pnad.faixa_etaria_31_03 = 2'
+    } else if (result == 4) {
+        return 'pnad.faixa_etaria_31_03 = 3'
+    } else if (result == 5) {
+        return 'pnad.faixa_etaria_31_03 = 4'
+    } else if (result == 6) {
+        return 'pnad.faixa_etaria_31_03 = 5'
+    }
+}
+
+liquidEnrollmentRatioApp.get('/', rqf.parse(),(req, res, next) => {
+    req.numerator = {};
+    req.denominator = {};
+    let liquidEnrollmentRatioApp = {};
+
+    req.sql.from('matricula')
+    .field('count(*)', 'total')
+    .field('matricula.ano_censo', 'year')
+    .group('matricula.ano_censo')
+    .order('matricula.ano_censo')
+    .where('matricula.tipo <= 3')
+
+    if ("education_level_short" in req.filter) {
+
+        if (Array.isArray(req.filter.education_level_short)) {
+            var stringQuery = '';
+            for(let i = 0; i < req.filter.education_level_short.length - 1; i++)  {
+                stringQuery = stringQuery + ConvertEnrollment(req.filter.education_level_short[i]) + ' OR ';
+            }
+
+            stringQuery = stringQuery + ConvertEnrollment(req.filter.education_level_short[req.filter.education_level_short.length - 1]);
+            delete req.filter.education_level_short;
+            req.sql.where(stringQuery);
+            req.sql.field('matricula.faixa_etaria_31_03', 'age_range')
+            req.sql.group('matricula.faixa_etaria_31_03', 'age_range');
+        }
+
+    } else if ( "education_level_short" in req.dims ) {
+
+        req.sql.field('matricula.faixa_etaria_31_03', 'age_range')
+        req.sql.where('(matricula.etapa_resumida = 1  AND matricula.faixa_etaria_31_03 = 1) OR (matricula.etapa_resumida = 2  AND matricula.faixa_etaria_31_03 = 2) OR (matricula.etapa_resumida = 3  AND matricula.faixa_etaria_31_03 = 3) OR (matricula.etapa_resumida = 4  AND matricula.faixa_etaria_31_03 = 4) OR (matricula.etapa_resumida = 5  AND matricula.faixa_etaria_31_03 = 5)');
+        req.sql.group('matricula.faixa_etaria_31_03', 'age_range');
+
+    } else {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Wrong/No filter specified'
+        });
+    }
+
+    next();
+}, rqf.build(), query, id2str.transform(),  (req, res, next) => {
+    req.numerator = req.result;
+    req.resetSql();
+    req.sql.field('sum(peso)', 'total')
+    .field('pnad.ano_censo','year')
+    .from('pnad')
+    .group('pnad.ano_censo')
+    .order('pnad.ano_censo')
+
+    //remove education_level_short how filter and add faixa_etaria_31_03 in filter
+
+    if ("education_level_short" in req.filter) {
+
+        if (Array.isArray(req.filter.education_level_short)) {
+            var stringQuery = '';
+            for(let i = 0; i < req.filter.education_level_short.length - 1; i++)  {
+                stringQuery = stringQuery + convertPnad(req.filter.education_level_short[i]) + ' OR ';
+            }
+            stringQuery = stringQuery + convertPnad(req.filter.education_level_short[req.filter.education_level_short.length - 1]);
+            req.sql.where(stringQuery);
+        }
+        req.sql.field('pnad.faixa_etaria_31_03', 'age_range')
+        req.sql.group('pnad.faixa_etaria_31_03', 'age_range');
+
+    } else if ( "education_level_short" in req.dims ) {
+
+        req.sql.field('pnad.faixa_etaria_31_03','age_range')
+        req.sql.where('pnad.faixa_etaria_31_03 = 1 OR pnad.faixa_etaria_31_03 = 2 OR pnad.faixa_etaria_31_03 = 3 OR pnad.faixa_etaria_31_03 = 4 OR pnad.faixa_etaria_31_03 = 5')
+        req.sql.group('pnad.faixa_etaria_31_03', 'age_range');
+    } else {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Wrong/No filter specified'
+        });
+    }
+
+    next();
+}, rqf.parse(), (req, res, next) => {
+    if ("education_level_short" in req.filter) {
+        delete req.filter.education_level_short;
+    }
+    if ("education_level_short" in req.dims) {
+        delete req.dims.education_level_short;
+    }
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    req.denominator = req.result;
+
+    //division to generate req.result final
+    req.result = []
+    let liquidEnrollment = matchQueries(req.denominator, req.numerator);
+    req.result = liquidEnrollment;
+    next();
+}, response('liquidEnrollmentRatio'));
+
+module.exports = liquidEnrollmentRatioApp;
diff --git a/src/libs/routes_v2/location.js b/src/libs/routes_v2/location.js
new file mode 100644
index 00000000..d493c452
--- /dev/null
+++ b/src/libs/routes_v2/location.js
@@ -0,0 +1,135 @@
+const express = require('express');
+
+const locationApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const passport = require('passport');
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+locationApp.use(cache('15 day'))
+
+let rqf = new ReqQueryFields();
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'id',
+    table: 'localizacao_escolas',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}, 'dims');
+
+locationApp.get('/school', rqf.parse(), (req, res, next) => {
+    req.dims.city=true;
+    req.dims.mesoregion=true;
+    req.dims.microregion=true;
+
+    req.sql.from('localizacao_escolas')
+        .field('localizacao_escolas.nome', 'name')
+        .field('localizacao_escolas.id', 'id')
+        .field('localizacao_escolas.latitude', 'latitude')
+        .field('localizacao_escolas.longitude', 'longitude')
+        .group('localizacao_escolas.nome')
+        .group('localizacao_escolas.id')
+        .group('localizacao_escolas.latitude')
+        .group('localizacao_escolas.longitude');
+    next();
+}, rqf.build(), query, id2str.transform(), response('location'));
+
+locationApp.get('/campi', rqf.parse(), (req, res, next) => {
+    req.dims.city=true;
+    req.dims.mesoregion=true;
+    req.dims.microregion=true;
+
+    req.sql.from('localizacao_campi')
+        .field('localizacao_campi.nome', 'name')
+        .field('localizacao_campi.id', 'id')
+        .field('localizacao_campi.ies_id', 'ies_id')
+        .field('localizacao_campi.latitude', 'latitude')
+        .field('localizacao_campi.longitude', 'longitude')
+        .group('localizacao_campi.nome')
+        .group('localizacao_campi.id')
+        .group('localizacao_campi.ies_id')
+        .group('localizacao_campi.latitude')
+        .group('localizacao_campi.longitude');
+    next();
+}, rqf.build(), query, id2str.transform(), response('location'));
+
+module.exports = locationApp;
\ No newline at end of file
diff --git a/src/libs/routes_v2/mesoregion.js b/src/libs/routes_v2/mesoregion.js
new file mode 100644
index 00000000..001976f2
--- /dev/null
+++ b/src/libs/routes_v2/mesoregion.js
@@ -0,0 +1,86 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const mesoregionApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+mesoregionApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'municipio'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: 'mesorregiao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id'
+    }
+});
+
+mesoregionApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('municipio')
+        .field('municipio.nome_mesorregiao', 'name')
+        .field('municipio.mesorregiao_id', 'id')
+        .field('municipio.estado_id', 'state_id')
+        .group('municipio.nome_mesorregiao')
+        .group('municipio.mesorregiao_id')
+        .group('municipio.estado_id')
+        .order('municipio.mesorregiao_id');
+    next();
+}, query, response('mesoregion'));
+
+module.exports = mesoregionApp;
diff --git a/src/libs/routes_v2/message.js b/src/libs/routes_v2/message.js
new file mode 100644
index 00000000..f9e0e330
--- /dev/null
+++ b/src/libs/routes_v2/message.js
@@ -0,0 +1,55 @@
+/*
+Copyright (C) 2021 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const messageApp = express.Router();
+
+const email = require(`../middlewares/email`);
+
+const log = require(`../log`)(module);
+
+messageApp.post('/', (req, res, next) => {
+    var reqName = req.body.name
+    var reqEmail = req.body.email
+    var reqContents = req.body.contents
+    var reqOrigin = req.body.origin ? req.body.origin : "";
+
+    var sub = "Contato " + reqOrigin
+    let mailOptions = {
+	to: ["dadoseducacionais@ufpr.br", reqEmail], 
+	from: `\"${reqName}\" <dadoseducacionais@ufpr.br>`,
+	text: reqContents,
+        subject: sub
+    }
+
+    email(mailOptions, (err, info) => {
+        if(err) {
+            log.error(err);
+            console.log(err);
+            res.status(500).json({msg: 'Undelivered Contact Mail'});
+        } else {
+            log.info(`Message ${info.messageId} sent: ${info.response}`);
+            res.json({msg: 'Contact Mail Successfully Delivered'});
+        }
+    });
+})
+
+module.exports = messageApp;
\ No newline at end of file
diff --git a/src/libs/routes_v2/microregion.js b/src/libs/routes_v2/microregion.js
new file mode 100644
index 00000000..c4aba0fe
--- /dev/null
+++ b/src/libs/routes_v2/microregion.js
@@ -0,0 +1,99 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const microregionApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+microregionApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'municipio'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: 'mesorregiao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: 'microrregiao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id'
+    }
+});
+
+microregionApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('municipio')
+        .field('municipio.nome_microrregiao', 'name')
+        .field('municipio.microrregiao_id', 'id')
+        .field('municipio.nome_mesorregiao', 'mesoregion_name')
+        .field('municipio.mesorregiao_id', 'mesoregion_id')
+        .field('municipio.estado_id', 'state_id')
+        .group('municipio.nome_microrregiao')
+        .group('municipio.microrregiao_id')
+        .group('municipio.nome_mesorregiao')
+        .group('municipio.mesorregiao_id')
+        .group('municipio.estado_id')
+        .order('municipio.microrregiao_id');
+    next();
+}, query, response('microregion'));
+
+module.exports = microregionApp;
diff --git a/src/libs/routes_v2/outOfSchool.js b/src/libs/routes_v2/outOfSchool.js
new file mode 100644
index 00000000..fcd57867
--- /dev/null
+++ b/src/libs/routes_v2/outOfSchool.js
@@ -0,0 +1,377 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const outOfSchoolApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+outOfSchoolApp.use(cache('15 day'));
+
+outOfSchoolApp.get('/year_range', (req, res, next) => {
+    req.sql.from('pnad')
+    .field('MIN(pnad.ano_censo)', 'start_year')
+    .field('MAX(pnad.ano_censo)', 'end_year')
+    .where('pnad.ano_censo >= 2007 AND pnad.ano_censo <= 2015');
+    next();
+}, query, response('range'));
+
+outOfSchoolApp.get('/years', (req, res, next) => {
+    req.sql.from('pnad').
+    field('DISTINCT pnad.ano_censo', 'year')
+    .where('pnad.ano_censo >= 2007 AND pnad.ano_censo <= 2015');
+    next();
+}, query, response('years'));
+
+outOfSchoolApp.get('/full_age_range', (req, res, next) => {
+    req.result = [
+        {id: 1, name: '0 a 3 anos'},
+        {id: 2, name: '4 a 5 anos'},
+        {id: 3, name: '6 a 10 anos'},
+        {id: 4, name: '11 a 14 anos'},
+        {id: 5, name: '15 a 17 anos'},
+        {id: 6, name: '18 a 24 anos'},
+        {id: 7, name: '25 a 29 anos'},
+        {id: 8, name: '30 a 40 anos'},
+        {id: 9, name: '41 a 50 anos'},
+        {id: 10, name: '51 a 64 anos'},
+        {id: 11, name: 'Mais de 64 anos'}
+    ];
+    next();
+}, response('full_age_range'));
+
+outOfSchoolApp.get('/ethnic_group', (req, res, next) => {
+    req.result = [
+        {id: 0, name: 'Sem declaração'},
+        {id: 1, name: 'Branca'},
+        {id: 2, name: 'Preta'},
+        {id: 3, name: 'Parda'},
+        {id: 4, name: 'Amarela'},
+        {id: 5, name: 'Indígena'}
+    ];
+    next();
+}, response('ethnic_group'));
+
+outOfSchoolApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+outOfSchoolApp.get('/gender', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Masculino'},
+        {id: 2, name: 'Feminino'}
+    ];
+    next();
+}, response('gender'));
+
+outOfSchoolApp.get('/fifth_household_income', (req, res, next) => {
+    req.result = [
+		{id: 1, name: '20% menores'},
+        {id: 2, name: '2o quinto'},
+        {id: 3, name: '3o quinto'},
+        {id: 4, name: '4o quinto'},
+		{id: 5, name: '20% maiores'},
+        {id: -1, name: 'Sem declaração'}
+	];
+    next();
+},response('fifth_household_income'));
+
+outOfSchoolApp.get('/extremes_household_income', (req, res, next) => {
+    req.result = [
+		{id: 1, name: '10% menores'},
+        {id: 2, name: '10% maiores'},
+        {id: -1, name: 'Sem declaração'}
+	];
+    next();
+}, response('extremes_household_income'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'pnad'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'pnad'
+    }
+}).addValue({
+    name: 'ethnic_group',
+    table: 'pnad',
+    tableField: 'cor_raca_id',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca_id'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'pnad',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'pnad',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'pnad',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'pnad',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'full_age_range',
+    table: 'pnad',
+    tableField: 'faixa_etaria_31_03',
+    resultField: 'full_age_range_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'faixa_etaria_31_03'
+    }
+}).addValue({
+    name: 'gender',
+    table: 'pnad',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'location',
+    table: 'pnad',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'extremes_household_income',
+    table: 'pnad',
+    tableField: 'extremos_nivel_rendimento',
+    resultField: 'extremes_household_income_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'extremos_nivel_rendimento'
+    }
+}).addValue({
+    name: 'fifth_household_income',
+    table: 'pnad',
+    tableField: 'quintil_nivel_rendimento',
+    resultField: 'fifth_household_income_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'quintil_nivel_rendimento'
+    }
+});
+
+outOfSchoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('pnad')
+    .field('SUM(pnad.peso)', 'total')
+    .field('pnad.ano_censo', 'year')
+    .where('pnad.escolaridade_familiar >= 1 AND pnad.escolaridade_familiar <= 4 AND pnad.frequenta_escola_creche = 4')
+    .group('pnad.ano_censo')
+    .order('pnad.ano_censo');
+
+    next();
+}, query, addMissing(rqf), id2str.transform(), response('out_of_school'));
+
+// Versão para o SimCAQ
+let simcaqRqf = new ReqQueryFields();
+
+simcaqRqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'brazil_total',
+    table: 'populacao_fora_da_escola',
+    tableField: 'brasil',
+    resultField: 'brazil_total_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'brasil'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: 'nome',
+    resultField: 'state_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'populacao_fora_da_escola'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'populacao_fora_da_escola'
+    }
+}).addValue({
+    name: 'pfe',
+    table: 'populacao_fora_da_escola',
+    tableField: 'codigo_pfe',
+    resultField: 'pfe_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'codigo_pfe'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'populacao_fora_da_escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'populacao_fora_da_escola',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'populacao_fora_da_escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'populacao_fora_da_escola',
+        field: 'ano_censo'
+    }
+});
+
+outOfSchoolApp.get('/simcaq', simcaqRqf.parse(), (req, res, next) => {
+    if ('state' in req.filter || 'city' in req.filter || 'state' in req.dims || 'city' in req.dims) { // Query in state/city level
+
+        if ('city' in req.filter && 'state' in req.filter) delete req.filter.state   // use only the city filter because of the table particularities
+
+        req.sql.from('populacao_fora_da_escola')
+        .field('SUM(populacao_fora_da_escola.pop_fora_escola)', 'total')
+        .field("'Brasil'", 'name')
+        .field('populacao_fora_da_escola.ano_censo')
+        .group('populacao_fora_da_escola.ano_censo')
+        .order('populacao_fora_da_escola.ano_censo');
+    } else { // Query in 'Brasil' level
+        req.sql.from('populacao_fora_da_escola')
+        .field('SUM(populacao_fora_da_escola.pop_fora_escola)', 'total')
+        .field("'Brasil'", 'name')
+        .field('populacao_fora_da_escola.ano_censo')
+        .where('populacao_fora_da_escola.brasil = 1')
+        .group('populacao_fora_da_escola.ano_censo')
+        .order('populacao_fora_da_escola.ano_censo'); 
+    }
+
+    next();
+}, simcaqRqf.build(), query, (req, res, next) => {
+	req.result.forEach((i) => {
+		i.total = parseInt(i.total);
+	});
+	next();
+}, addMissing(simcaqRqf), id2str.transform(), response('out_of_school'));
+
+module.exports = outOfSchoolApp;
diff --git a/src/libs/routes_v2/pibpercapita.js b/src/libs/routes_v2/pibpercapita.js
new file mode 100644
index 00000000..c5c77a92
--- /dev/null
+++ b/src/libs/routes_v2/pibpercapita.js
@@ -0,0 +1,250 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const pibpercapitaApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+pibpercapitaApp.use(cache('15 day'));
+
+pibpercapitaApp.get('/year_range', (req, res, next) => {
+    req.sql.from('ibge_pib')
+    .field('MIN(ibge_pib.ano_censo)', 'start_year')
+    .field('MAX(ibge_pib.ano_censo)', 'end_year')
+    .where('ibge_pib.ano_censo > 2013');
+    next();
+}, query, response('range'));
+
+pibpercapitaApp.get('/years', (req, res, next) => {
+    req.sql.from('ibge_pib').
+    field('DISTINCT ibge_pib.ano_censo', 'year')
+    .where('ibge_pib.ano_censo > 2013');
+    next();
+}, query, response('years'));
+
+pibpercapitaApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'ibge_pib\'');
+    next();
+}, query, response('source'));
+
+pibpercapitaApp.get('/income_level', (req, res, next) => {
+    req.result = [
+        {id: 1, name: "1º quintil – 20% menores"},
+        {id: 2, name: "2º quintil"},
+        {id: 3, name: "3º quintil"},
+        {id: 4, name: "4º quintil"},
+        {id: 5, name: "5º quintil – 20% maiores"},
+    ];
+    next();
+}, response('income_level'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'ibge_pib'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'ibge_pib'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+      relation: '=',
+      type: 'integer',
+      field: 'regiao_id',
+      table: 'ibge_pib'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'ibge_pib'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'income_level',
+    table: 'ibge_pib',
+    tableField: 'nivel_renda_per_capita',
+    resultField: 'income_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'nivel_renda_per_capita'
+    }
+}).addValue({
+    name: 'income_level_brasil',
+    table: 'ibge_pib',
+    tableField: 'nivel_renda_brasil',
+    resultField: 'income_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'nivel_renda_brasil'
+    }
+}).addValue({
+    name: 'income_level_uf',
+    table: 'ibge_pib',
+    tableField: 'nivel_renda_uf',
+    resultField: 'income_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'nivel_renda_uf'
+    }
+});;
+
+pibpercapitaApp.get('/', rqf.parse(), (req, res, next) => {
+    if ('income_level' in req.dims) {   // Retorna os quintis
+        delete req.dims.income_level
+
+        req.sql.from('pib_quintis')
+        .field('pib_quintis.valor', 'total')
+        .field('pib_quintis.tipo', 'income_level_id')
+        .field('pib_quintis.ano_censo', 'year')
+        .group('pib_quintis.ano_censo')
+        .group('pib_quintis.valor')
+        .group('pib_quintis.tipo')
+        .order('pib_quintis.tipo')
+
+        if (!('state' in req.filter)) {
+            req.sql.where('pib_quintis.estado_id = 0')
+        } 
+        if ('city' in req.filter) {
+            req.sql.join('ibge_pib', null, 'ibge_pib.nivel_renda_brasil=pib_quintis.tipo AND ibge_pib.ano_censo=pib_quintis.ano_censo')
+        }
+    }
+
+    else if (("city" in req.dims) || ("city" in req.filter)) {
+      req.sql.from('ibge_pib')
+      .field('ibge_pib.pib_per_capita', 'total')
+      .field('ibge_pib.ano_censo', 'year')
+      .group('ibge_pib.ano_censo')
+      .group('ibge_pib.pib_per_capita')
+      .order('ibge_pib.ano_censo')
+  } else  {
+      req.sql.from('ibge_pib')
+      .field('SUM(ibge_pib.pib)/SUM(ibge_pib.populacao)', 'total')
+      .field('ibge_pib.ano_censo', 'year')
+      .group('ibge_pib.ano_censo')
+      .order('ibge_pib.ano_censo')
+   }
+   next();
+}, rqf.build(), query, (req, res, next) => {
+     req.result.forEach((i) => {
+        let value = parseFloat(i.total);
+        let isnum = /^\d+$/.test(value);
+        if (isnum == true) {
+            value = value.toFixed(2)
+        }
+        // console.log(i.total);
+
+        let res = value.toString().split(".");
+        //rounding decimal.
+        let decimal = Math.round(res[1].toString().substring(0,2) + (".") + res[1].toString().substring(2,3));
+        //case 0 after comma
+        if (res[1].toString().substring(0,1) == 0) {
+            i.total = parseFloat(res[0] + "." + "0" + decimal);
+        } else {
+            i.total = parseFloat(res[0] + "." +  decimal);
+        }
+        // console.log(i.total);
+     });
+     next();
+ }, id2str.transform(false), response("pibpercapita"));
+
+
+module.exports = pibpercapitaApp;
diff --git a/src/libs/routes_v2/population.js b/src/libs/routes_v2/population.js
new file mode 100644
index 00000000..fca3b0d6
--- /dev/null
+++ b/src/libs/routes_v2/population.js
@@ -0,0 +1,163 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const populationApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+populationApp.use(cache('15 day'));
+
+populationApp.get('/year_range', (req, res, next) => {
+    req.sql.from('ibge_pib')
+    .field('MIN(ibge_pib.ano_censo)', 'start_year')
+    .field('MAX(ibge_pib.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+populationApp.get('/years', (req, res, next) => {
+    req.sql.from('ibge_pib').
+    field('DISTINCT ibge_pib.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+// populationApp.get('/city_size', (req, res, next) => {
+//     req.result = [
+//         {id: 1, name: "0 - 5000"},
+//         {id: 2, name: "5001 - 10000"},
+//         {id: 3, name: "10001 - 20000"},
+//         {id: 4, name: "20001 - 50000"},
+//         {id: 5, name: "50001 - 100000"},
+//         {id: 6, name: "100001 - 500000"},
+//         {id: 7, name: "mais que 500000"}
+//     ];
+//     next();
+// }, response('city_size'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'ibge_pib'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'ibge_pib'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'ibge_pib'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'ibge_pib'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'regiao_id',
+        table: 'ibge_pib'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'ibge_pib'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'ibge_pib',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'ibge_pib',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+});
+
+populationApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+  req.sql.from('ibge_pib')
+    .field('SUM(ibge_pib.populacao)', 'total')
+    .field('ibge_pib.ano_censo', 'year')
+    .group('ibge_pib.ano_censo')
+    .order('ibge_pib.ano_censo')
+
+   next();
+}, query, addMissing(rqf), id2str.transform(false), response('population'));
+
+module.exports = populationApp;
diff --git a/src/libs/routes_v2/portalMec.js b/src/libs/routes_v2/portalMec.js
new file mode 100644
index 00000000..e0e5353e
--- /dev/null
+++ b/src/libs/routes_v2/portalMec.js
@@ -0,0 +1,136 @@
+const express = require('express');
+
+const portalMecApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'day',
+    table: 'docente',
+    tableField: 'nasc_dia',
+    resultField: 'born_day_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'docente',
+        field: 'nasc_dia'
+    }
+}).addValue({
+    name: 'month',
+    table: 'docente',
+    tableField: 'nasc_mes_id',
+    resultField: 'born_month',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'docente',
+        field: 'nasc_mes'
+    }
+}).addValue({
+    name: 'year',
+    table: 'docente',
+    tableField: 'nasc_ano_id',
+    resultField: 'born_year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'docente',
+        field: 'nasc_ano'
+    }
+}).addValue({
+    name: 'teacher',
+    table: 'docente',
+    tableField: 'id',
+    resultField: 'teacher_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'docente',
+        field: 'id'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'docente',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'docente',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'docente',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'docente',
+        field: 'ano_censo'
+    }
+});
+
+portalMecApp.get('/', rqf.parse(), (req, res, next) => {
+
+    req.sql.field('docente.id_docente')
+    .field('docente.ano_censo', 'year')
+    .field('docente.cod_quimica', 'Química')
+    .field('docente.cod_fisica', 'Física')
+    .field('docente.cod_matematica', 'Matemática')
+    .field('docente.cod_biologia', 'Biologia')
+    .field('docente.cod_ciencias', 'Ciências')
+    .field('docente.cod_literat_port', 'Língua/ Literatura Portuguesa')
+    .field('docente.cod_literat_ing', 'Língua/ Literatura estrangeira - Inglês')
+    .field('docente.cod_literat_esp', 'Língua/ Literatura estrangeira - Espanhol')
+    .field('docente.cod_literat_frances', 'Língua/ Literatura estrangeira - Francês')
+    .field('docente.literat_outra', 'Língua/ Literatura estrangeira - Outra')
+    .field('docente.cod_literat_indigena', 'Língua/ Literatura estrangeira - Língua Indígena')
+    .field('docente.cod_artes', 'Artes (Educação Artística, Teatro, Dança, Música, Artes Plásticas e outras)')
+    .field('docente.cod_ed_fisica', 'Educação Física')
+    .field('docente.cod_hist', 'História')
+    .field('docente.cod_geo', 'Geografia')
+    .field('docente.cod_filos', 'Filosofia')
+    .field('docente.cod_ensino_religioso', 'Ensino Religioso')
+    .field('docente.cod_estudos_sociais', 'Estudos Sociais')
+    .field('docente.cod_sociologia', 'Sociologia')
+    .field('docente.cod_inf_comp', 'Informática/ Computação')
+    .field('docente.cod_profissionalizante', 'Disciplinas profissionalizantes')
+    .field('docente.cod_disc_atendimento_especiais', 'Disciplinas voltadas ao atendimento às necessidades educacionais específicas dos alunos que são público alvo da educação especial e às práticas educacionais inclusivas')
+    .field('docente.cod_disc_diversidade_socio_cult', 'Disciplinas voltadas à diversidade sociocultural (Disciplinas pedagógicas)')
+    .field('docente.cod_libras', 'Libras')
+    .field('docente.cod_disciplina_pedag', 'Disciplinas pedagógicas')
+    .field('docente.cod_outras_disciplina', 'Outras disciplinas')
+    .from('docente')
+    next();
+
+}, rqf.build(), query, response('portalMec'));
+
+module.exports = portalMecApp;
diff --git a/src/libs/routes_v2/portalMecInep.js b/src/libs/routes_v2/portalMecInep.js
new file mode 100644
index 00000000..2b235891
--- /dev/null
+++ b/src/libs/routes_v2/portalMecInep.js
@@ -0,0 +1,60 @@
+const express = require('express');
+
+const portalMecInepApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'school_cod',
+    table: 'escola',
+    tableField: 'id',
+    resultField: 'school_cod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'escola',
+        field: 'id'
+    }
+});
+
+portalMecInepApp.get('/', rqf.parse(), (req, res, next) => {
+
+    req.sql.field('DISTINCT escola.id', 'id')
+    .field('escola.nome_escola', 'name')
+    .from('escola')
+    .join('estado', null, 'estado.id=escola.estado_id')
+    .field('estado.nome', 'state_name')
+    .join('municipio', null, 'municipio.id=escola.municipio_id')
+    .field('municipio.nome', 'city_name')
+
+    next();
+
+}, rqf.build(), query, response('portalMec_inep'));
+
+module.exports = portalMecInepApp;
diff --git a/src/libs/routes_v2/rateSchool.js b/src/libs/routes_v2/rateSchool.js
new file mode 100644
index 00000000..a6a91b7b
--- /dev/null
+++ b/src/libs/routes_v2/rateSchool.js
@@ -0,0 +1,337 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const rateSchoolApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const multiQuery = require(`${libs}/middlewares/multiQuery`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+rateSchoolApp.use(cache('15 day'));
+
+let rqf = new ReqQueryFields();
+
+// Complete range of the enrollments dataset.
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+rateSchoolApp.get('/year_range', (req, res, next) => {
+    req.sql.from('pnad')
+    .field('MIN(pnad.ano_censo)', 'start_year')
+    .field('MAX(pnad.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+rateSchoolApp.get('/years', (req, res, next) => {
+    req.sql.from('pnad')
+    .field('DISTINCT pnad.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+rateSchoolApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'pnad\'');
+    next();
+}, query, response('source'));
+
+rateSchoolApp.get('/ethnic_group_pnad', (req, res, next) => {
+    req.result = [
+        {id: 0, name: 'Indígena'},
+        {id: 1, name: 'Branca e amarela'},
+        {id: 2, name: 'Preta e parda'},
+        {id: 9, name: 'Sem declaração'}
+    ];
+    next();
+}, response('ethnic_group_pnad'));
+
+rateSchoolApp.get('/age_range', (req, res, next) => {
+    req.result = [
+        {id: 1, name: '0 a 3 anos'},
+        {id: 2, name: '4 a 5 anos'},
+        {id: 3, name: '6 a 10 anos'},
+        {id: 4, name: '11 a 14 anos'},
+        {id: 5, name: '15 a 17 anos'},
+        {id: 6, name: '18 a 24 anos'}
+    ];
+    next();
+}, response('age_range'));
+
+rateSchoolApp.get('/gender', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Masculino'},
+        {id: 2, name: 'Feminino'}
+    ];
+    next();
+}, response('gender'));
+
+rateSchoolApp.get('/location', (req, res, next) => {
+    req.result = [
+		{id: 1, name: 'Urbana'},
+		{id: 2, name: 'Rural'}
+	];
+    next();
+}, response('location'));
+
+rateSchoolApp.get('/fifth_household_income', (req, res, next) => {
+    req.result = [
+		{id: 1, name: '20% menores'},
+        {id: 2, name: '2o quinto'},
+        {id: 3, name: '3o quinto'},
+        {id: 4, name: '4o quinto'},
+		{id: 5, name: '20% maiores'},
+        {id: -1, name: 'Sem declaração'}
+	];
+    next();
+},response('fifth_household_income'));
+
+rateSchoolApp.get('/extremes_household_income', (req, res, next) => {
+    req.result = [
+		{id: 1, name: '10% menores'},
+        {id: 2, name: '10% maiores'},
+        {id: -1, name: 'Sem declaração'}
+	];
+    next();
+}, response('extremes_household_income'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'pnad'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'pnad'
+    }
+}).addValue({
+    name: 'ethnic_group_pnad',
+    table: 'pnad',
+    tableField: 'cor_raca',
+    resultField: 'ethnic_group_pnad_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'pnad',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'pnad',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'pnad',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'pnad',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'age_range',
+    table: 'pnad',
+    tableField: 'faixa_etaria_31_03',
+    resultField: 'age_range_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'faixa_etaria_31_03'
+    }
+}).addValue({
+    name: 'gender',
+    table: 'pnad',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'location',
+    table: 'pnad',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'extremes_household_income',
+    table: 'pnad',
+    tableField: 'extremos_nivel_rendimento',
+    resultField: 'extremes_household_income_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'extremos_nivel_rendimento'
+    }
+}).addValue({
+    name: 'fifth_household_income',
+    table: 'pnad',
+    tableField: 'quintil_nivel_rendimento',
+    resultField: 'fifth_household_income_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'quintil_nivel_rendimento'
+    }
+});
+
+function matchQueries(queryTotal, queryPartial) {
+    let match = [];
+    queryTotal.forEach((result) => {
+        let newObj = {};
+        let keys = Object.keys(result);
+        keys.forEach((key) => {
+            newObj[key] = result[key];
+        });
+        // console.log('NEW OBJ');
+        // console.log(newObj);
+        let index = keys.indexOf('total');
+        if(index > -1) keys.splice(index, 1);
+        let objMatch = null;
+
+        for(let i = 0; i < queryPartial.length; ++i) {
+            let partial = queryPartial[i];
+            let foundMatch = true;
+            for(let j = 0; j < keys.length; ++j) {
+                let key = keys[j];
+                if(partial[key] !== result[key]) {
+                    foundMatch = false;
+                    break;
+                }
+            }
+            if(foundMatch) {
+                objMatch = partial;
+                break;
+            }
+        }
+
+        if(objMatch) {
+            // console.log(objMatch);
+            newObj.denominator = result.total;
+            newObj.partial = objMatch.total;
+            newObj.total = (objMatch.total / result.total) * 100;
+            match.push(newObj);
+        }
+    });
+
+    return match;
+}
+
+rateSchoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.querySet = [];
+    req.queryIndex = {};
+
+    log.debug(req.sql.toParam());
+    if ("age_range" in req.filter || "age_range" in req.dims) {
+        let freq_total = req.sql.clone();
+        freq_total.field('sum(pnad.peso)', 'total')
+        .field('pnad.ano_censo','year')
+        .from('pnad')
+        .group('pnad.ano_censo')
+        .order('pnad.ano_censo')
+        .where('pnad.faixa_etaria_31_03 < 7')
+        req.queryIndex.freq_total = req.querySet.push(freq_total) - 1;
+
+        let freq_nursery = req.sql.clone();
+        freq_nursery.field('sum(pnad.peso)', 'total')
+        .field('pnad.ano_censo','year')
+        .from('pnad')
+        .group('pnad.ano_censo')
+        .order('pnad.ano_censo')
+        .where('pnad.frequenta_escola_creche = 2')
+        .where('pnad.faixa_etaria_31_03 < 7')
+        req.queryIndex.freq_nursery = req.querySet.push(freq_nursery) - 1;
+    }
+     next();
+}, multiQuery, (req, res, next) => {
+    if ("age_range" in req.filter || "age_range" in req.dims) {
+        log.debug(req.result[req.queryIndex.freq_total]);
+        log.debug(req.result[req.queryIndex.freq_nursery])
+        let school_attendance_rate = matchQueries(req.result[req.queryIndex.freq_total], req.result[req.queryIndex.freq_nursery]);
+        req.result = school_attendance_rate;
+    } else {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Wrong/No filter specified'
+        });
+    }
+    log.debug(req.result)
+    next();
+}, id2str.transform(false), response('rateSchool'));
+
+module.exports = rateSchoolApp;
diff --git a/src/libs/routes_v2/region.js b/src/libs/routes_v2/region.js
new file mode 100644
index 00000000..5df65e72
--- /dev/null
+++ b/src/libs/routes_v2/region.js
@@ -0,0 +1,72 @@
+const express = require('express');
+
+const regionApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+regionApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'id',
+    table: '@',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id',
+        table: '@'
+    }
+}).addValue({
+    name: 'id_not',
+    table: '@',
+    tableField: 'id',
+    where: {
+        relation: '<>',
+        type: 'integer',
+        field: 'id',
+        table: '@'
+    }
+}).addField({
+    name: 'search',
+    field: false,
+    where: true
+}).addValueToField({
+    name: 'name',
+    table: '@',
+    tableField: 'nome',
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'nome',
+        table: '@'
+    }
+}, 'search');
+
+regionApp.get('/', rqf.parse(), (req, res, next) => {
+    req.sql.from('regiao')
+        .field('id')
+        .field('nome', 'name');
+    next();
+}, rqf.build(), query, response('region'));
+
+module.exports = regionApp;
diff --git a/src/libs/routes_v2/resetToken.js b/src/libs/routes_v2/resetToken.js
new file mode 100644
index 00000000..34ece845
--- /dev/null
+++ b/src/libs/routes_v2/resetToken.js
@@ -0,0 +1,81 @@
+const express = require('express');
+
+const resetTokenApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const ResetToken = require(`${libs}/models/resetToken`);
+
+const User = require(`${libs}/models/user`);
+
+resetTokenApp.get('/:token', (req, res, next) => {
+    let token = req.params.token;
+    ResetToken.findOne({token: token}, (err, rToken) => {
+        if(err) {
+            log.error(err);
+            return next(err);
+        }
+        if(!rToken) {
+            // TODO: generate new reset token
+            res.statusCode = 404;
+            return next({msg: 'Token not found', status:404});
+        }
+        if (rToken.hasExpired()) {
+            res.statusCode = 410;
+            ResetToken.remove({token: token}, (err) => {
+                if(err) {
+                    log.error(err);
+                    next(err);
+                }
+            })
+            return next({msg: 'Token expired', status: 410});
+        }
+        User.findById(rToken.userId, (err, user) => {
+            if(err) {
+                log.error(err);
+                next(err);
+            }
+            let u = user.toObject();
+            delete u.salt;
+            delete u.hashedPassword;
+            res.json({user: u});
+        });
+    });
+});
+resetTokenApp.post('/:token', (req, res, next) => {
+    let token = req.params.token;
+    ResetToken.findOne({token: token}, (err, rToken) => {
+        if(err) {
+            log.error(err);
+            return next(err);
+        }
+        if(!rToken) {
+            res.statusCode = 404;
+            return next({msg: 'Token not found', status:404});
+        }
+        User.findById(rToken.userId, (err, user) => {
+            if(err) {
+                log.error(err);
+                next(err);
+            }
+            user.password = req.body.password;
+            user.save((err) => {
+                if(err) {
+                    log.error(err);
+                    next(err);
+                }
+                ResetToken.remove({token: token}, (err) => {
+                    if(err) {
+                        log.error(err);
+                        next(err);
+                    }
+                })
+                res.json({msg: "Senha alterada com sucesso"});
+            })
+        });
+    });
+})
+
+module.exports = resetTokenApp;
diff --git a/src/libs/routes/school.js b/src/libs/routes_v2/school.js
similarity index 100%
rename from src/libs/routes/school.js
rename to src/libs/routes_v2/school.js
diff --git a/src/libs/routes_v2/schoolInfrastructure.js b/src/libs/routes_v2/schoolInfrastructure.js
new file mode 100644
index 00000000..c74f0e30
--- /dev/null
+++ b/src/libs/routes_v2/schoolInfrastructure.js
@@ -0,0 +1,741 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const infrastructureApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const multiQuery = require(`${libs}/middlewares/multiQuery`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+infrastructureApp.use(cache('15 day'));
+
+infrastructureApp.get('/year_range', (req, res, next) => {
+    req.sql.from('escola')
+    .field('MIN(escola.ano_censo)', 'start_year')
+    .field('MAX(escola.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+infrastructureApp.get('/years', (req, res, next) => {
+    req.sql.from('escola')
+    .field('DISTINCT escola.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+infrastructureApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'escola\'');
+    next();
+}, query, response('source'));
+
+infrastructureApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+infrastructureApp.get('/rural_location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: "Urbana"},
+        {id: 2, name: "Rural"},
+        {id: 3, name: "Rural - Área de assentamento"},
+        {id: 4, name: "Rural - Terra indígena"},
+        {id: 5, name: "Rural - Área remanescente de quilombos"},
+        {id: 6, name: "Rural - Unidade de uso sustentável"}
+    ];
+    next();
+}, response('rural_location'));
+
+infrastructureApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+infrastructureApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'id',
+    resultField: 'city_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'escola'
+    }
+}, 'filter').addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'escola'
+    }
+}, 'dims').addValueToField({
+    name: 'state',
+    table: 'estado',
+    tableField: 'id',
+    resultField: 'state_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'escola'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'escola'
+    }
+}, 'filter').addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'escola'
+    }
+}).addValue({
+    name: 'location',
+    table: 'escola',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'escola',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'escola',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'escola',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+});
+
+function matchQueries(queryTotal, queryPartial, queryNeeded, zeroPercentage=false) {
+    let match = [];
+    queryTotal.forEach((result) => {
+        let newObj = {};
+        let keys = Object.keys(result);
+        keys.forEach((key) => {
+            newObj[key] = result[key];
+        });
+        let index = keys.indexOf('total');
+        if(index > -1) keys.splice(index, 1);
+        let partialMatch = null;
+        let needMatch = null;
+
+        for(let i = 0; i < queryPartial.length; ++i) {
+            let partial = queryPartial[i];
+            let foundMatch = true;
+            for(let j = 0; j < keys.length; ++j) {
+                let key = keys[j];
+                if(partial[key] !== result[key]) {
+                    foundMatch = false;
+                    break;
+                }
+            }
+            if(foundMatch) {
+                partialMatch = partial;
+                break;
+            }
+        }
+        if(queryPartial.length == 0) {
+            partialMatch = JSON.parse(JSON.stringify(result));
+            partialMatch.total = 0;
+        }
+
+        for(let i = 0; i < queryNeeded.length; ++i) {
+            let needed = queryNeeded[i];
+            let foundMatch = true;
+            for(let j = 0; j < keys.length; ++j) {
+                let key = keys[j];
+                if(needed[key] !== result[key]) {
+                    foundMatch = false;
+                    break;
+                }
+            }
+            if(foundMatch) {
+                needMatch = needed;
+                break;
+            }
+        }
+
+        if(queryNeeded.length == 0) {
+            needMatch = JSON.parse(JSON.stringify(result));
+            needMatch.total = 0;
+        }
+
+        newObj.percentage = ( ( partialMatch ? partialMatch.total : 0) / result.total) * 100;
+		if(zeroPercentage) newObj.percentage = 0;
+		newObj.partial = ( partialMatch ? partialMatch.total : 0);
+		newObj.total = result.total;
+		newObj.need_adaptation = needMatch ? needMatch.total : 0;
+		match.push(newObj);
+    });
+
+    return match;
+}
+
+infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.querySet = [];
+    req.queryIndex = {};
+
+    // Local de funcionamento
+    let allSchools = req.sql.clone();
+    allSchools.from('escola').field('COUNT(escola.id)', 'total')
+    .field("'Brasil'", 'name')
+    .field('escola.ano_censo', 'year')
+    .group('escola.ano_censo')
+    .where('escola.situacao_de_funcionamento = 1')
+    .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1')
+    .where('escola.local_func_predio_escolar = 1')
+    .where('escola.dependencia_adm_id <= 3')
+    .order('escola.ano_censo');
+    req.queryIndex.allSchools = req.querySet.push(allSchools) - 1;
+
+    let allUrbanSchools = allSchools.clone();
+    allUrbanSchools.where('escola.localizacao_id = 1');
+    req.queryIndex.allUrbanSchools = req.querySet.push(allUrbanSchools) - 1;
+
+    let allCountrySchools = allSchools.clone();
+    allCountrySchools.where('escola.localizacao_id = 2');
+    req.queryIndex.allCountrySchools = req.querySet.push(allCountrySchools) - 1;
+
+    let allSchoolsNotSchoolBuilding = req.sql.clone();
+    allSchoolsNotSchoolBuilding.from('escola').field('COUNT(escola.id)', 'total')
+    .field("'Brasil'", 'name')
+    .field('escola.ano_censo', 'year')
+    .group('escola.ano_censo')
+    .where('escola.situacao_de_funcionamento = 1')
+    .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1')
+    .where('escola.local_func_predio_escolar = 0')
+    .where('escola.dependencia_adm_id <= 3')
+    .order('escola.ano_censo');
+    req.queryIndex.allSchoolsNotSchoolBuilding = req.querySet.push(allSchoolsNotSchoolBuilding) - 1;
+
+    // Bibliotecas
+    req.queryIndex.allLibraries = req.queryIndex.allUrbanSchools;
+
+    let haveLibraries = allUrbanSchools.clone();
+    haveLibraries.where('escola.biblioteca = 1');
+    req.queryIndex.haveLibraries = req.querySet.push(haveLibraries) - 1;
+
+    let needLibraries = allUrbanSchools.clone();
+    needLibraries.where('escola.biblioteca = 0');
+    req.queryIndex.needLibraries = req.querySet.push(needLibraries) - 1;
+
+    // Sala de leitura
+    req.queryIndex.allLibrariesReadingRoom = req.queryIndex.allCountrySchools;
+
+    let haveLibrariesReadingRoom = allCountrySchools.clone();
+    haveLibrariesReadingRoom.where('escola.biblioteca_sala_leitura = true');
+    req.queryIndex.haveLibrariesReadingRoom = req.querySet.push(haveLibrariesReadingRoom) - 1;
+
+    let needLibrariesReadingRoom = allCountrySchools.clone();
+    needLibrariesReadingRoom.where('escola.biblioteca_sala_leitura = false');
+    req.queryIndex.needLibrariesReadingRoom = req.querySet.push(needLibrariesReadingRoom) - 1;
+
+    // Laboratório de informática
+    // Se (situacao_de_funcionamento=1) & (dependencia_adm_id<=3) & (CEBES027P1=1) & 
+    // (ensino_regular=1 | ensino_eja=1 |    educacao_profissional=1) & 
+    // ( reg_fund_ai_t1=1 | reg_fund_af_t1=1 | reg_medio_medio_t1=1 | ensino_eja_fund= 1 |     ensino_eja_medio= 1 | ensino_eja_prof= 1 | esp_eja_fund=1 | 
+    // esp_eja_medio=1 | ensino_esp_exclusiva_eja_prof=1) então conta id
+    let allInfLab = allSchools.clone();
+    allInfLab.where('reg_fund_ai_t1=1 OR reg_fund_af_t1=1 OR reg_medio_medio_t1=1 OR ensino_eja_fund=1 OR ensino_eja_medio=1 OR ensino_eja_prof=1 OR esp_eja_fund=1 OR esp_eja_medio=1 OR ensino_esp_exclusiva_eja_prof=1');
+    req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1;
+
+    let haveInfLab = allInfLab.clone();
+    haveInfLab.where('escola.lab_informatica = 1');
+    req.queryIndex.haveInfLab = req.querySet.push(haveInfLab) - 1;
+
+    let needInfLab = allInfLab.clone();
+    needInfLab.where('escola.lab_informatica = 0');
+    req.queryIndex.needInfLab = req.querySet.push(needInfLab) - 1;
+
+    // Laboratório de ciências
+    let allScienceLab = allSchools.clone();
+    allScienceLab.where('reg_fund_af_t1=1 OR reg_medio_medio_t1=1 OR ensino_eja_fund=1 OR ensino_eja_medio=1 OR ensino_eja_prof=1 OR esp_eja_fund=1 OR esp_eja_medio=1 OR ensino_esp_exclusiva_eja_prof=1');
+    req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1;
+
+    let haveScienceLab = allScienceLab.clone();
+    haveScienceLab.where('escola.lab_ciencias = true');
+    req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1;
+
+    let needScienceLab = allScienceLab.clone();
+    needScienceLab.where('escola.lab_ciencias = false');
+    req.queryIndex.needScienceLab = req.querySet.push(needScienceLab) - 1;
+
+    // Parque infantil
+    // Se (situacao_de_funcionamento=1) and (ensino_regular=1 OR ensino_eja=1 OR educacao_profissional=1) and 
+    // (local_func_predio_escolar=1) and (dependencia_adm_id<=3) and (reg_infantil_creche_t1=1 or reg_infantil_preescola_t1=1 or reg_fund_ai_t1=1) então conta id
+    let allKidsPark = allSchools.clone();
+    allKidsPark.where('reg_infantil_creche_t1=1 OR reg_infantil_preescola_t1=1 OR reg_fund_ai_t1=1');
+    req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1;
+
+    let haveKidsPark = allKidsPark.clone();
+    haveKidsPark.where('escola.parque_infantil = 1');
+    req.queryIndex.haveKidsPark = req.querySet.push(haveKidsPark) - 1;
+
+    let needKidsPark = allKidsPark.clone();
+    needKidsPark.where('escola.parque_infantil = 0');
+    req.queryIndex.needKidsPark = req.querySet.push(needKidsPark) - 1;
+
+    // // Berçário
+    // let allCribs = allSchools.clone();
+    // allCribs.where('escola.reg_infantil_creche_t1 = 1');
+    // req.queryIndex.allCribs = req.querySet.push(allCribs) - 1;
+
+    // let haveCribs = allCribs.clone();
+    // haveCribs.where('escola.bercario = 1');
+    // req.queryIndex.haveCribs = req.querySet.push(haveCribs) - 1;
+
+    // let needCribs = allCribs.clone();
+    // needCribs.where('escola.bercario = 0');
+    // req.queryIndex.needCribs = req.querySet.push(needCribs) - 1;
+
+    // Quadra de esportes
+    let allSportsCourt = allSchools.clone();
+    allSportsCourt.where('reg_fund_ai_t1=1  or reg_fund_af_t1=1 or  reg_medio_medio_t1=1  or  ensino_eja_fund= 1 or ensino_eja_medio= 1 or ensino_eja_prof= 1 or esp_eja_fund=1 or esp_eja_medio=1 or ensino_esp_exclusiva_eja_prof=1');
+    req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1;
+
+    let haveSportsCourt = allSportsCourt.clone();
+    haveSportsCourt.where('escola.quadra_esportes = 1');
+    req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1;
+
+    let needSportsCourt = allSportsCourt.clone();
+    needSportsCourt.where('escola.quadra_esportes = 0');
+    req.queryIndex.needSportsCourt = req.querySet.push(needSportsCourt) - 1;
+
+    // Quadras a serem cobertas
+    // Se (situacao_de_funcionamento=1) and (ensino_regular=1 OR ensino_eja=1 OR educacao_profissional=1) and (local_func_predio_escolar=1) and 
+    // (dependencia_adm_id<=3) and (reg_fund_ai_t1=1  or reg_fund_af_t1=1 or  reg_medio_medio_t1=1  or  ensino_eja_fund= 1 or ensino_eja_medio= 1 or 
+    // ensino_eja_prof= 1 or esp_eja_fund=1 or esp_eja_medio=1 or ensino_esp_exclusiva_eja_prof=1) and (quadra_esportes_descoberta=1) então conta id
+    let allSportsCourtCoverage = allSportsCourt.clone();
+    allSportsCourtCoverage.where('escola.quadra_esportes_descoberta = 1');
+    req.queryIndex.allSportsCourtCoverage = req.querySet.push(allSportsCourtCoverage) -1;
+
+    req.queryIndex.haveSportsCourtCoverage = req.queryIndex.allSportsCourtCoverage; // It must be []
+
+    req.queryIndex.needSportsCourtCoverage = req.queryIndex.allSportsCourtCoverage;
+
+    // Pátio
+    req.queryIndex.allCourtyard = req.queryIndex.allSchools;
+
+    let haveCourtyard = allSchools.clone();
+    haveCourtyard.where('escola.patio = 1 OR escola.patio = 2');
+    req.queryIndex.haveCourtyard = req.querySet.push(haveCourtyard) - 1;
+
+    let needCourtyard = allSchools.clone();
+    needCourtyard.where('escola.patio = 0');
+    req.queryIndex.needCourtyard = req.querySet.push(needCourtyard) - 1;
+
+    // Pátios a serem cobertos
+    let allCourtyardCoverage = allSchools.clone();
+    allCourtyardCoverage.where('escola.patio = 1');
+    req.queryIndex.allCourtyardCoverage = req.querySet.push(allCourtyardCoverage) - 1;
+
+    req.queryIndex.haveCourtyardCoverage = req.queryIndex.allCourtyardCoverage; // It must be []
+
+    req.queryIndex.needCourtyardCoverage = req.queryIndex.allCourtyardCoverage;
+
+    // Sala de direção
+    req.queryIndex.allDirectorRoom = req.queryIndex.allUrbanSchools;
+
+    let haveDirectorRoom = allUrbanSchools.clone();
+    haveDirectorRoom.where('escola.sala_diretoria = 1');
+    req.queryIndex.haveDirectorRoom = req.querySet.push(haveDirectorRoom) - 1;
+
+    let needDirectorRoom = allUrbanSchools.clone();
+    needDirectorRoom.where('escola.sala_diretoria = 0');
+    req.queryIndex.needDirectorRoom = req.querySet.push(needDirectorRoom) - 1;
+
+    // Secretaria
+    req.queryIndex.allSecretary = req.queryIndex.allSchools;
+
+    let haveSecretary = allSchools.clone();
+    haveSecretary.where('escola.secretaria = 1');
+    req.queryIndex.haveSecretary = req.querySet.push(haveSecretary) - 1;
+
+    let needSecretary = allSchools.clone();
+    needSecretary.where('escola.secretaria = 0');
+    req.queryIndex.needSecretary = req.querySet.push(needSecretary) - 1;
+
+    // Sala de professores
+    req.queryIndex.allTeacherRoom = req.queryIndex.allSchools;
+
+    let haveTeacherRoom = allSchools.clone();
+    haveTeacherRoom.where('escola.sala_professor = 1');
+    req.queryIndex.haveTeacherRoom = req.querySet.push(haveTeacherRoom) - 1;
+
+    let needTeacherRoom = allSchools.clone();
+    needTeacherRoom.where('escola.sala_professor = 0');
+    req.queryIndex.needTeacherRoom = req.querySet.push(needTeacherRoom) - 1;
+
+    // Cozinha
+    req.queryIndex.allKitchen = req.queryIndex.allSchools;
+
+    let haveKitchen = allSchools.clone();
+    haveKitchen.where('escola.cozinha = 1');
+    req.queryIndex.haveKitchen = req.querySet.push(haveKitchen) - 1;
+
+    let needKitchen = allSchools.clone();
+    needKitchen.where('escola.cozinha = 0');
+    req.queryIndex.needKitchen = req.querySet.push(needKitchen) - 1;
+
+    // Despensa
+    req.queryIndex.allStoreroom = req.queryIndex.allSchools;
+
+    let haveStoreroom = allSchools.clone();
+    haveStoreroom.where('escola.despensa = 1');
+    req.queryIndex.haveStoreroom = req.querySet.push(haveStoreroom) - 1;
+
+    let needStoreroom = allSchools.clone();
+    needStoreroom.where('escola.despensa = 0');
+    req.queryIndex.needStoreroom = req.querySet.push(needStoreroom) - 1;
+
+    // Almoxarifado
+    req.queryIndex.allWarehouse = req.queryIndex.allSchools;
+
+    let haveWarehouse = allSchools.clone();
+    haveWarehouse.where('escola.almoxarifado = 1');
+    req.queryIndex.haveWarehouse = req.querySet.push(haveWarehouse) - 1;
+
+    let needWarehouse = allSchools.clone();
+    needWarehouse.where('escola.almoxarifado = 0');
+    req.queryIndex.needWarehouse = req.querySet.push(needWarehouse) - 1;
+
+    // Internet
+    req.queryIndex.allInternet = req.queryIndex.allCountrySchools;
+
+    let haveInternet = allCountrySchools.clone();
+    haveInternet.where('escola.internet = 1');
+    req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1;
+
+    let needInternet = allCountrySchools.clone();
+    needInternet.where('escola.internet = 0');
+    req.queryIndex.needInternet = req.querySet.push(needInternet) - 1;
+
+    // Internet banda larga
+    // Se (situacao_de_funcionamento=1) and (ensino_regular=1 OR ensino_eja=1 OR educacao_profissional=1) and (local_func_predio_escolar=1) and 
+    // (dependencia_adm_id<=3) and (localizacao_id=2) então conta id
+    req.queryIndex.allBroadbandInternet = req.queryIndex.allUrbanSchools;
+
+    let haveBroadbandInternet = allUrbanSchools.clone();
+    haveBroadbandInternet.where('escola.internet_banda_larga = 1');
+    req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1;
+
+    let needBroadbandInternet = allUrbanSchools.clone();
+    needBroadbandInternet.where('escola.internet_banda_larga = 0');
+    req.queryIndex.needBroadbandInternet = req.querySet.push(needBroadbandInternet) - 1;
+
+    // Banheiro
+    req.queryIndex.allInsideBathroom = req.queryIndex.allSchools;
+
+    let haveInsideBathroom = allSchools.clone();
+    haveInsideBathroom.where('escola.banheiro = 1');
+    req.queryIndex.haveInsideBathroom = req.querySet.push(haveInsideBathroom) - 1;
+
+    let needInsideBathroom = allSchools.clone();
+    needInsideBathroom.where('escola.banheiro = 0');
+    req.queryIndex.needInsideBathroom = req.querySet.push(needInsideBathroom) - 1;
+
+    // Banheiro adequado para educação infantil dentro do prédio
+    // Se (situacao_de_funcionamento=1) and (ensino_regular=1 OR ensino_eja=1 OR educacao_profissional=1) and (local_func_predio_escolar=1) and
+    // (dependencia_adm_id<=3) and (reg_infantil_creche_t1=1 or reg_infantil_preescola_t1=1 or  reg_fund_ai_t1=1) então conta id
+    let allInsideKidsBathroom = allSchools.clone();
+    allInsideKidsBathroom.where('reg_infantil_creche_t1=1 OR reg_infantil_preescola_t1=1 OR reg_fund_ai_t1=1');
+    req.queryIndex.allInsideKidsBathroom = req.querySet.push(allInsideKidsBathroom) - 1;
+
+    let haveInsideKidsBathroom = allInsideKidsBathroom.clone();
+    haveInsideKidsBathroom.where('escola.sanitario_ei = 1');
+    req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1;
+
+    let needInsideKidsBathroom = allInsideKidsBathroom.clone();
+    needInsideKidsBathroom.where('escola.sanitario_ei = 0');
+    req.queryIndex.needInsideKidsBathroom = req.querySet.push(needInsideKidsBathroom) - 1;
+
+    // Fornecimento de energia
+    req.queryIndex.allEletricPower = req.queryIndex.allSchools;
+
+    let haveEletricPower = allSchools.clone();
+    haveEletricPower.where('escola.energia_inexistente = 0');
+    req.queryIndex.haveEletricPower = req.querySet.push(haveEletricPower) - 1;
+
+    let needEletricPower = allSchools.clone();
+    needEletricPower.where('escola.energia_inexistente = 1');
+    req.queryIndex.needEletricPower = req.querySet.push(needEletricPower) - 1;
+
+    // Abastecimento de água
+    req.queryIndex.allWaterSupply = req.queryIndex.allSchools;
+
+    let haveWaterSupply = allSchools.clone();
+    haveWaterSupply.where('escola.agua_inexistente = 0');
+    req.queryIndex.haveWaterSupply = req.querySet.push(haveWaterSupply) - 1;
+
+    let needWaterSupply = allSchools.clone();
+    needWaterSupply.where('escola.agua_inexistente = 1');
+    req.queryIndex.needWaterSupply = req.querySet.push(needWaterSupply) - 1;
+
+    // Água Potável
+    req.queryIndex.allFilteredWater = req.queryIndex.allSchools;
+
+    let haveFilteredWater = allSchools.clone();
+    haveFilteredWater.where('escola.agua_potavel = 1');
+    req.queryIndex.haveFilteredWater = req.querySet.push(haveFilteredWater) - 1;
+
+    let needFilteredWater = allSchools.clone();
+    needFilteredWater.where('escola.agua_potavel = 0');
+    req.queryIndex.needFilteredWater = req.querySet.push(needFilteredWater) - 1;
+
+    // Coleta de esgoto
+    req.queryIndex.allSewage = req.queryIndex.allSchools;
+
+    let haveSewage = allSchools.clone();
+    haveSewage.where('escola.esgoto_rede_publica = 1 OR escola.esgoto_fossa_septica = 1');
+    req.queryIndex.haveSewage = req.querySet.push(haveSewage) - 1;
+
+    let needSewage = allSchools.clone();
+    needSewage.where('escola.esgoto_rede_publica = 0 AND escola.esgoto_fossa_septica = 0');
+    req.queryIndex.needSewage = req.querySet.push(needSewage) - 1;
+
+    // Dependências adaptada para pessoas com deficiências
+    req.queryIndex.allAdaptedBuilding = req.queryIndex.allSchools;
+
+    let haveAdaptedBuilding = allSchools.clone();
+    haveAdaptedBuilding.where('escola.acessibilidade_inexistente = 0');
+    req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1;
+
+    let needAdaptedBuilding = allSchools.clone();
+    needAdaptedBuilding.where('escola.acessibilidade_inexistente = 1');
+    req.queryIndex.needAdaptedBuilding = req.querySet.push(needAdaptedBuilding) - 1;
+
+    // Banheiros adaptados para pessoas com deficiências
+    req.queryIndex.allSpecialBathroom = req.queryIndex.allSchools;
+
+    let haveSpecialBathroom = allSchools.clone();
+    haveSpecialBathroom.where('escola.sanitario_pne = 1');
+    req.queryIndex.haveSpecialBathroom = req.querySet.push(haveSpecialBathroom) - 1;
+
+    let needSpecialBathroom = allSchools.clone();
+    needSpecialBathroom.where('escola.sanitario_pne = 0');
+    req.queryIndex.needSpecialBathroom = req.querySet.push(needSpecialBathroom) - 1;
+
+
+    next();
+}, multiQuery, (req, res, next) => {
+
+    let schools_in_school_buildings = req.result[req.queryIndex.allSchools];
+    let urban_schools_in_school_buildings = req.result[req.queryIndex.allUrbanSchools];
+    let country_schools_in_school_buildings = req.result[req.queryIndex.allCountrySchools];
+    let schools_not_in_school_buildings = req.result[req.queryIndex.allSchoolsNotSchoolBuilding];
+
+    // Faz o matching entre os resultados
+    let libraries = matchQueries(req.result[req.queryIndex.allLibraries], req.result[req.queryIndex.haveLibraries], req.result[req.queryIndex.needLibraries]);
+    let libraries_reading_room = matchQueries(req.result[req.queryIndex.allLibrariesReadingRoom], req.result[req.queryIndex.haveLibrariesReadingRoom], req.result[req.queryIndex.needLibrariesReadingRoom]);
+    let computer_lab = matchQueries(req.result[req.queryIndex.allInfLab], req.result[req.queryIndex.haveInfLab], req.result[req.queryIndex.needInfLab]);
+    let science_lab = matchQueries(req.result[req.queryIndex.allScienceLab], req.result[req.queryIndex.haveScienceLab], req.result[req.queryIndex.needScienceLab]);
+    let kids_park = matchQueries(req.result[req.queryIndex.allKidsPark], req.result[req.queryIndex.haveKidsPark], req.result[req.queryIndex.needKidsPark]);
+    // let nursery = matchQueries(req.result[req.queryIndex.allCribs], req.result[req.queryIndex.haveCribs], req.result[req.queryIndex.needCribs]);
+    let sports_court = matchQueries(req.result[req.queryIndex.allSportsCourt], req.result[req.queryIndex.haveSportsCourt], req.result[req.queryIndex.needSportsCourt]);
+    let sports_court_coverage = matchQueries(req.result[req.queryIndex.allSportsCourtCoverage], [], req.result[req.queryIndex.needSportsCourtCoverage], true); // have = []
+    let courtyard = matchQueries(req.result[req.queryIndex.allCourtyard], req.result[req.queryIndex.haveCourtyard], req.result[req.queryIndex.needCourtyard]);
+    let courtyard_coverage = matchQueries(req.result[req.queryIndex.allCourtyardCoverage], [], req.result[req.queryIndex.needCourtyardCoverage], true); // have = []
+    let director_room = matchQueries(req.result[req.queryIndex.allDirectorRoom], req.result[req.queryIndex.haveDirectorRoom], req.result[req.queryIndex.needDirectorRoom]);
+    let secretary = matchQueries(req.result[req.queryIndex.allSecretary], req.result[req.queryIndex.haveSecretary], req.result[req.queryIndex.needSecretary]);
+    let teacher_room = matchQueries(req.result[req.queryIndex.allTeacherRoom], req.result[req.queryIndex.haveTeacherRoom], req.result[req.queryIndex.needTeacherRoom]);
+    let kitchen = matchQueries(req.result[req.queryIndex.allKitchen], req.result[req.queryIndex.haveKitchen], req.result[req.queryIndex.needKitchen]);
+    let storeroom = matchQueries(req.result[req.queryIndex.allStoreroom], req.result[req.queryIndex.haveStoreroom], req.result[req.queryIndex.needStoreroom]);
+    let warehouse = matchQueries(req.result[req.queryIndex.allWarehouse], req.result[req.queryIndex.haveWarehouse], req.result[req.queryIndex.needWarehouse]);
+    let internet = matchQueries(req.result[req.queryIndex.allInternet], req.result[req.queryIndex.haveInternet], req.result[req.queryIndex.needInternet]);
+    let broadband_internet = matchQueries(req.result[req.queryIndex.allBroadbandInternet], req.result[req.queryIndex.haveBroadbandInternet], req.result[req.queryIndex.needBroadbandInternet]);
+    let inside_bathroom = matchQueries(req.result[req.queryIndex.allInsideBathroom], req.result[req.queryIndex.haveInsideBathroom], req.result[req.queryIndex.needInsideBathroom]);
+    let inside_kids_bathroom = matchQueries(req.result[req.queryIndex.allInsideKidsBathroom], req.result[req.queryIndex.haveInsideKidsBathroom], req.result[req.queryIndex.needInsideKidsBathroom]);
+    let eletrical_power = matchQueries(req.result[req.queryIndex.allEletricPower], req.result[req.queryIndex.haveEletricPower], req.result[req.queryIndex.needEletricPower]);
+    let water_supply = matchQueries(req.result[req.queryIndex.allWaterSupply], req.result[req.queryIndex.haveWaterSupply], req.result[req.queryIndex.needWaterSupply]);
+    let filtered_water = matchQueries(req.result[req.queryIndex.allFilteredWater], req.result[req.queryIndex.haveFilteredWater], req.result[req.queryIndex.needFilteredWater]);
+    let sewage = matchQueries(req.result[req.queryIndex.allSewage], req.result[req.queryIndex.haveSewage], req.result[req.queryIndex.needSewage]);
+    let adapted_building = matchQueries(req.result[req.queryIndex.allAdaptedBuilding], req.result[req.queryIndex.haveAdaptedBuilding], req.result[req.queryIndex.needAdaptedBuilding]);
+    let adapted_bathroom = matchQueries(req.result[req.queryIndex.allSpecialBathroom], req.result[req.queryIndex.haveSpecialBathroom], req.result[req.queryIndex.needSpecialBathroom]);
+
+    req.result = [{
+        schools_in_school_buildings,
+        urban_schools_in_school_buildings,
+        country_schools_in_school_buildings,
+        schools_not_in_school_buildings,
+        libraries,
+        libraries_reading_room,
+        computer_lab,
+        science_lab,
+        kids_park,
+        sports_court,
+        sports_court_coverage,
+        courtyard,
+        courtyard_coverage,
+        director_room,
+        secretary,
+        teacher_room,
+        kitchen,
+        storeroom,
+        warehouse,
+        internet,
+        broadband_internet,
+        inside_bathroom,
+        inside_kids_bathroom,
+        eletric_energy: eletrical_power,
+        water_supply,
+        filtered_water,
+        sewage_treatment: sewage,
+        adapted_building,
+        special_bathroom: adapted_bathroom
+    }];
+
+    next();
+}, id2str.multitransform(false), response('infrastructure'));
+
+module.exports = infrastructureApp;
diff --git a/src/libs/routes_v2/schoolLocation.js b/src/libs/routes_v2/schoolLocation.js
new file mode 100644
index 00000000..5c0d98ff
--- /dev/null
+++ b/src/libs/routes_v2/schoolLocation.js
@@ -0,0 +1,114 @@
+const express = require('express');
+
+const schoolLocationApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const passport = require('passport');
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'id',
+    table: 'localizacao_escolas',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'localizacao_escolas'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'localizacao_escolas'
+    }
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: '@'
+    }
+}, 'dims');
+
+schoolLocationApp.get('/', rqf.parse(), (req, res, next) => {
+    req.dims.city=true;
+    req.dims.mesoregion=true;
+    req.dims.microregion=true;
+
+    req.sql.from('localizacao_escolas')
+        .field('localizacao_escolas.nome', 'name')
+        .field('localizacao_escolas.id', 'id')
+        .field('localizacao_escolas.latitude', 'latitude')
+        .field('localizacao_escolas.longitude', 'longitude')
+        .group('localizacao_escolas.nome')
+        .group('localizacao_escolas.id')
+        .group('localizacao_escolas.latitude')
+        .group('localizacao_escolas.longitude');
+    next();
+}, rqf.build(), query, id2str.transform(), response('school_location'));
+
+module.exports = schoolLocationApp;
diff --git a/src/libs/routes_v2/simulation.js b/src/libs/routes_v2/simulation.js
new file mode 100644
index 00000000..4b2e40c3
--- /dev/null
+++ b/src/libs/routes_v2/simulation.js
@@ -0,0 +1,167 @@
+const express = require('express');
+
+const simulationApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const Simulation = require(`${libs}/models/simulation`);
+
+const PQR = require(`${libs}/models/pqr`);
+
+const passport = require('passport');
+
+simulationApp.get('/time', (req, res, next) => {
+    const maxTime = parseInt(req.query.max_time, 10);
+    if(isNaN(maxTime)) {
+        res.status(400);
+        next({
+            status: 400,
+            message: 'Invalid value for mandatory parameter max_time'
+        });
+    }
+    res.json({
+        result: Array.apply(null, {length: maxTime}).map(Number.call, Number).map((i)=>i+1)
+    });
+});
+
+simulationApp.get('/pqr', (req, res) => {
+    PQR.findOne((err, pqr) => {
+        if(err) {
+            log.error(err);
+            return next({err});
+        }
+
+        res.json(pqr);
+    });
+});
+
+simulationApp.put('/pqr', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+    let user = req.user.toObject();
+
+    PQR.findOne((err, pqr) => {
+        if(err) {
+            log.error(err)
+            return next({err});
+        }
+
+        if(!user.admin) {
+            log.info(`Usuário ${user.email} tentou alterar o PQR, mas não tem privilégio`);
+            res.statusCode = 401;
+            return next({err: { msg: 'Unauthorized'}});
+        }
+        pqr.content = req.body.content || pqr.content;
+        pqr.save((err) => {
+            if(err) {
+                log.error(err);
+                return next({err});
+            }
+            res.json({msg: 'PQR updated'})
+        });
+    });
+});
+
+simulationApp.get('/', passport.authenticate('bearer', { session: false }), (req, res) => {
+    let user = req.user.toObject();
+    let query = Simulation.find({userId: user._id}).select('userId name createdAt updatedAt');
+    query.exec((err, simulations) => {
+        if(err) {
+            log.error(err);
+            return next({err});
+        }
+
+        res.json(simulations);
+    });
+
+    // Simulation.find({userId: user._id}, (err, simulations) => {
+    //     if(err) {
+    //         log.error(err);
+    //         return next({err});
+    //     }
+
+    //     res.json(simulations);
+    // });
+});
+
+simulationApp.post('/', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+    let user = req.user.toObject();
+
+    let simulation = new Simulation({
+        userId: user._id,
+        content: req.body.content,
+        name: req.body.name
+    });
+
+    simulation.save((err) => {
+        if(err) {
+            log.error(err);
+            return next({err});
+        }
+
+        res.json({msg: 'Simulation created', simulation});
+    })
+});
+
+simulationApp.get('/:id', passport.authenticate('bearer', { session: false }), (req, res) => {
+    let user = req.user.toObject();
+
+    Simulation.findOne({_id: req.params.id, userId: user._id}, (err, simulation) => {
+        if(err) {
+            log.error(err);
+            return next({err});
+        }
+
+        res.json(simulation);
+    });
+});
+
+simulationApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+    let user = req.user.toObject();
+
+    Simulation.findOne({_id: req.params.id, userId: user._id}, (err, simulation) => {
+        if(err) {
+            log.error(err);
+            return next({err});
+        }
+
+        if(!simulation) {
+            res.statusCode = 404;
+            return next({err: { msg: 'Simulation not found'}});
+        }
+
+        simulation.content = req.body.content || simulation.content;
+        simulation.name = req.body.name || simulation.name;
+        simulation.updatedAt = Date.now();
+
+        simulation.save((err) => {
+            if(err) {
+                log.error(err);
+                return next(err);
+            }
+
+            res.json(simulation);
+        });
+    });
+});
+
+simulationApp.delete('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+    let user = req.user.toObject();
+
+    Simulation.remove({_id: req.params.id, userId: user._id}, (err, simulation) => {
+        if(err) {
+            log.error(err);
+            return next({err});
+        }
+
+        res.json({msg: 'Simulation removed'});
+    });
+});
+
+module.exports = simulationApp;
diff --git a/src/libs/routes_v2/siope.js b/src/libs/routes_v2/siope.js
new file mode 100644
index 00000000..1ef9f101
--- /dev/null
+++ b/src/libs/routes_v2/siope.js
@@ -0,0 +1,186 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const siopeApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const multiQuery = require(`${libs}/middlewares/multiQuery`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+siopeApp.use(cache('15 day'));
+
+siopeApp.get('/years', (req, res, next) => {
+    req.sql.from('siope_mun')
+    .field('DISTINCT siope_mun.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    req.oldResult = req.result;
+
+    req.sql = squel.select();
+
+    req.sql.from('siope_uf')
+    .field('DISTINCT siope_uf.ano_censo', 'year');
+    next();
+}, query, (req, res, next) => {
+    let result = Object.assign(req.oldResult, req.result);
+    req.result = result;
+    next();
+}, response('years'));
+
+rqf.addField({
+    name: 'filter',
+    field: true,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'siope_mun'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'siope_mun'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['id','nome','sigla'],
+    resultField: ['state_id','state_name','state_abbreviation'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+});
+
+
+
+siopeApp.get('/', rqf.parse(), (req, res, next) => {
+    req.querySet = [];
+    req.queryIndex = {};
+
+    let siopeUf = req.sql.clone();
+    siopeUf.from('siope_uf')
+    .field('siope_uf.ano_censo', 'year')
+    .field('siope_uf.estado_id', 'state_id')
+    .field('siope_uf.fundeb', 'fundeb')
+    .field('siope_uf.total_impostos', 'impostos')
+    .field('siope_uf.total_mde', 'MDE')
+    .group('siope_uf.ano_censo')
+    .group('siope_uf.estado_id')
+    .group('siope_uf.fundeb')
+    .group('siope_uf.total_impostos')
+    .group('siope_uf.total_mde')
+    .order('siope_uf.ano_censo');
+    req.queryIndex.siopeUf = req.querySet.push(siopeUf) - 1;
+
+    let siopeMun = req.sql.clone();
+    siopeMun.from('siope_mun')
+    .field('siope_mun.ano_censo', 'year')
+    .field('siope_mun.estado_id', 'state_id')
+    .field('siope_mun.municipio_id', 'city_id')
+    .field('siope_mun.fundeb', 'fundeb')
+    .field('siope_mun.total_impostos', 'impostos')
+    .field('siope_mun.total_mde', 'MDE')
+    .group('siope_mun.ano_censo')
+    .group('siope_mun.estado_id')
+    .group('siope_mun.municipio_id')
+    .group('siope_mun.fundeb')
+    .group('siope_mun.total_impostos')
+    .group('siope_mun.total_mde')
+    .order('siope_mun.ano_censo');
+    req.queryIndex.siopeMun = req.querySet.push(siopeMun) - 1;
+
+    next();
+}, rqf.multibuild(), multiQuery, (req, res, next) => {
+
+    let result = []
+
+    req.result[req.queryIndex.siopeUf].forEach((item) => {
+        result.push(item)
+    });
+    req.result[req.queryIndex.siopeMun].forEach((item) => {
+        result.push(item)
+    });
+
+    req.result = result;
+    next();
+
+}, response('siope'));
+
+module.exports = siopeApp;
diff --git a/src/libs/routes_v2/spatial.js b/src/libs/routes_v2/spatial.js
new file mode 100644
index 00000000..d4f48fe8
--- /dev/null
+++ b/src/libs/routes_v2/spatial.js
@@ -0,0 +1,373 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const log = require(`${libs}/log`)(module);
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const sqlQuery = require(`${libs}/db/query_exec`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const spatialApp = express();
+
+let rqf = new ReqQueryFields();
+
+function processResultSet(querySet, querySetLabels = ["result"], singleResult = false) {
+    const resultMap = new Map();
+    let resultIdx = 0;
+    // loop relies on the fact that Promise.all maintains the order of the original iterable
+    for(let result of querySet) {
+        const resultLbl = querySetLabels[resultIdx];
+        resultMap[resultLbl] = [];
+        if (singleResult) {
+            resultMap[resultLbl] = result[0];
+        } else {
+            for(let row of result) {
+                resultMap[resultLbl].push(row);
+            }
+        }
+        resultIdx++;
+    }
+    return resultMap;
+}
+
+function dbExecAll(querySet = []) {
+    // Issue all queries concurrently to the database, for every query object in the iterable
+    // NOTE: Array.map() returns a copy of the original array with each object 'mapped'.
+    return querySet.map((qry) => { return sqlQuery(qry.toString()); });
+}
+
+rqf.addField({
+    name: 'filter',
+    field: true,
+    where: true
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id'
+    }
+}).addValue({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+});
+
+spatialApp.get('/sociodemographic', rqf.parse(), rqf.build(), (req, res, next) => {
+    const populationYearQry = squel.select()
+        .field('MAX(ibge_populacao.ano_censo)')
+        .from('ibge_populacao');
+
+    const populationQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('SUM(populacao)', 'population')
+        .field('ibge_populacao.ano_censo', 'census_year')
+        .from('ibge_populacao')
+        .where(`ibge_populacao.ano_censo IN (${populationYearQry.toString()})`)
+        .group('ibge_populacao.ano_censo');
+
+    const pibYearQry = squel.select()
+        .field('MAX(ibge_pib.ano_censo)')
+        .from('ibge_pib');
+
+    const pibQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(ibge_pib.pib_per_capita)', 'gdp_per_capita')
+        .field('ibge_pib.ano_censo', 'census_year')
+        .from('ibge_pib')
+        .where(`ibge_pib.ano_censo IN (${pibYearQry.toString()})`)
+        .group('ibge_pib.ano_censo');
+
+    const idhYearQry = squel.select()
+        .field('MAX(adh_idh.ano_censo)')
+        .from('adh_idh');
+
+    const idhQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(idhm)', 'idhm')
+        .field('adh_idh.ano_censo', 'census_year')
+        .from('adh_idh')
+        .where(`adh_idh.ano_censo IN (${idhYearQry.toString()})`)
+        .group('adh_idh.ano_censo');
+
+    const analfabYearQry = squel.select()
+        .field('MAX(adh_analfabetismo.ano_censo)')
+        .from('adh_analfabetismo');
+
+    const analfabQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(t_analf15m)', 'analfabetism')
+        .field('adh_analfabetismo.ano_censo', 'census_year')
+        .from('adh_analfabetismo')
+        .where(`adh_analfabetismo.ano_censo IN (${analfabYearQry.toString()})`)
+        .group('adh_analfabetismo.ano_censo');
+
+    const giniYearQry = squel.select()
+        .field('MAX(adh_gini.ano_censo)')
+        .from('adh_gini');
+
+    const giniQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('AVG(gini)', 'gini')
+        .field('adh_gini.ano_censo', 'census_year')
+        .from('adh_gini')
+        .where(`adh_gini.ano_censo IN (${giniYearQry.toString()})`)
+        .group('adh_gini.ano_censo');
+
+    // map query objects to their respective response labels
+    const queryLabels = [ "population", "gdp", "idh", "analfab", "gini" ];
+    const querySet = [ populationQry, pibQry, idhQry, analfabQry, giniQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels, true);
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('spatial'));
+
+spatialApp.get('/educational', rqf.parse(), rqf.build(), (req, res, next) => {
+    const censusYearQry = squel.select()
+        .field('MAX(escola.ano_censo)', 'ano_censo')
+        .from('escola')
+        .toString();
+
+    const totalSchoolsQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('\'Total\'', 'location_name')
+        .field('COUNT(DISTINCT(escola.id))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .from('turma')
+        .from('escola')
+        .where('escola.ano_censo=turma.ano_censo AND escola.id=turma.escola_id')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('turma.tipo_turma_id = 0')
+        .group('escola.ano_censo');
+
+    const schoolsPerLocationQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COUNT(DISTINCT(escola.id))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .field('localizacao.descricao', 'location_name')
+        .from('localizacao')
+        .from('turma')
+        .from('escola')
+        .where('escola.localizacao_id=localizacao.id')
+        .where('escola.ano_censo=turma.ano_censo AND escola.id=turma.escola_id')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .where('turma.tipo_turma_id = 0')
+        .group('escola.localizacao_id')
+        .group('escola.ano_censo')
+        .group('localizacao.descricao')
+        .order('localizacao.descricao');
+
+    const schoolsPerAdmDependencyQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COUNT(DISTINCT(escola.id))', 'total')
+        .field('escola.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency_name')
+        .from('dependencia_adm')
+        .from('escola')
+        .where('escola.dependencia_adm_id=dependencia_adm.id')
+        .where(`escola.ano_censo IN (${censusYearQry})`)
+        .group('escola.ano_censo')
+        .group('dependencia_adm.nome')
+        .order('escola.ano_censo')
+        .order('dependencia_adm.nome');
+
+    const enrollmentsQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(uc201.matriculas), 0)', 'total')
+        .field('uc201.ano_censo', 'census_year')
+        .from('uc201')
+        .group('uc201.ano_censo');
+
+    const enrollmentsPerAdmDepQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(uc201.matriculas), 0)', 'total')
+        .field('uc201.ano_censo', 'census_year')
+        .field('dependencia_adm.nome', 'adm_dependency_name')
+        .from('dependencia_adm')
+        .from('uc201')
+        .where('uc201.dependencia_adm_id=dependencia_adm.id')
+        .group('dependencia_adm.nome')
+        .group('uc201.ano_censo');
+
+    const enrollmentsPerSchoolLevelQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(uc201.matriculas), 0)', 'total')
+        .field('uc201.ano_censo', 'census_year')
+        .field('etapa_ensino.desc_etapa', 'school_level_name')
+        .field('dependencia_adm.nome', 'adm_dependency_name')
+        .from('etapa_ensino')
+        .from('dependencia_adm')
+        .from('uc201')
+        .where('uc201.etapas_mod_ensino_segmento_id=etapa_ensino.id')
+        .where('uc201.dependencia_adm_id=dependencia_adm.id')
+        .group('etapa_ensino.desc_etapa')
+        .group('dependencia_adm.nome')
+        .group('uc201.ano_censo');
+
+    const enrollmentsPerLocationQry = req.sql.clone()
+        .field('\'Brasil\'', 'name')
+        .field('COALESCE(SUM(uc201.matriculas), 0)', 'total')
+        .field('uc201.ano_censo', 'census_year')
+        .field('localizacao.descricao', 'location_name')
+        .from('localizacao')
+        .from('uc201')
+        .where('uc201.localizacao=localizacao.id')
+        .group('localizacao.descricao')
+        .group('uc201.ano_censo');
+
+    const queryLabels = [ "school", "school_per_location", "school_per_adm_dependency", "enrollment", "enrollment_per_adm_dep",
+        "enrollment_per_school_level", "enrollment_per_location" ];
+    const querySet = [ totalSchoolsQry, schoolsPerLocationQry, schoolsPerAdmDependencyQry, enrollmentsQry,
+        enrollmentsPerAdmDepQry, enrollmentsPerSchoolLevelQry, enrollmentsPerLocationQry];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet)).then((queryResults) => {
+        req.result = processResultSet(queryResults, queryLabels);
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('spatial'));
+
+spatialApp.get('/educational/school_level', rqf.parse(), rqf.build(), (req, res, next) => {
+    const enrollmentsPerSchoolLevelYearQry = squel.select()
+        .field('MAX(matricula.ano_censo)', 'census_year')
+        .from('matricula');
+
+    const enrollmentsPerSchoolLevelQry = req.sql.clone()
+        .field('COALESCE(COUNT(matricula.id), 0)', 'total')
+        .field('matricula.ano_censo', 'census_year')
+        .field('matricula.serie_ano_id', 'school_year')
+        .field('etapa_ensino.desc_etapa', 'school_level')
+        .from('etapa_ensino')
+        .from('matricula')
+        .where(`matricula.ano_censo IN (${enrollmentsPerSchoolLevelYearQry.toString()})`)
+        .where('matricula.etapa_ensino_id = etapa_ensino.id')
+        .where('matricula.tipo <= 3')
+        .group('etapa_ensino.desc_etapa')
+        .group('etapa_ensino.id')
+        .group('matricula.serie_ano_id')
+        .group('matricula.ano_censo')
+        .order('etapa_ensino.id')
+        .order('matricula.serie_ano_id')
+        .order('matricula.ano_censo');
+
+    const queryLabels = [ 'enrollment_per_school_level', 'enrollment_census_year' ];
+    const querySet = [ enrollmentsPerSchoolLevelQry, enrollmentsPerSchoolLevelYearQry ];
+    // wait until all queries finish or one of them fail
+    Promise.all(dbExecAll(querySet, enrollmentsPerSchoolLevelYearQry)).then((queryResults) => {
+        const result = queryResults[0];
+        const censusYear = queryResults[1][0]['census_year'];
+
+        let school_levels = {};
+        for(let i = 0; i < result.length; ++i) {
+            const school_year  = id2str.schoolYear(result[i].school_year);
+            const school_level = result[i].school_level;
+            const census_year = result[i].census_year;
+            if (typeof school_levels[school_level] === 'undefined') {
+                school_levels[school_level] = {};
+            }
+            school_levels[school_level][school_year] = parseInt(result[i].total, 10);
+        }
+
+        let response = [];
+        for(let level in school_levels) {
+            if (school_levels.hasOwnProperty(level)) {
+                let sclevel = {};
+                sclevel["degree"] = level;
+                sclevel["census_year"] = parseInt(censusYear, 10);
+                sclevel["table"] = [];
+                for(let school_year in school_levels[level]) {
+                    if (school_levels[level].hasOwnProperty(school_year)) {
+                        let enrollment = { 'title' : school_year,
+                                           'value' : school_levels[level][school_year] };
+                        sclevel["table"].push(enrollment);
+                    }
+                }
+                response.push(sclevel);
+            }
+        }
+        req.result = response;
+        next();
+    }).catch((error) => {
+        log.error(`[SQL query error] ${error}`);
+        next(error);
+    });
+}, response('spatial'));
+
+module.exports = spatialApp;
diff --git a/src/libs/routes_v2/state.js b/src/libs/routes_v2/state.js
new file mode 100644
index 00000000..c9830b20
--- /dev/null
+++ b/src/libs/routes_v2/state.js
@@ -0,0 +1,108 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const stateApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+stateApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'id',
+    table: 'estado',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'id_not',
+    table: 'estado',
+    tableField: 'id',
+    where: {
+        relation: '<>',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'regiao_id',
+        table: 'estado'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'estado'
+    }
+}).addField({
+    name: 'search',
+    field: false,
+    where: true
+}).addValueToField({
+    name: 'name',
+    table: 'estado',
+    tableField: 'nome',
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'nome'
+    }
+}, 'search');
+
+stateApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('estado')
+        .field('estado.id').group('estado.id')
+        .field('regiao_id', 'region_id').group('regiao_id')
+        .field('estado.nome', 'name').group('estado.nome')
+        .field('estado.sigla', 'abbreviation').group('estado.sigla')
+        .field('estado.longitude', 'longitude').group('estado.longitude')
+        .field('estado.latitude', 'latitude').group('estado.latitude')
+				.where('estado.id <> 99');
+    next();
+}, query, response('state'));
+
+module.exports = stateApp;
diff --git a/src/libs/routes_v2/studentsAee.js b/src/libs/routes_v2/studentsAee.js
new file mode 100644
index 00000000..2a45a2f5
--- /dev/null
+++ b/src/libs/routes_v2/studentsAee.js
@@ -0,0 +1,219 @@
+const express = require('express');
+
+const studentsAeeApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const passport = require('passport');
+
+const download = require(`${libs}/middlewares/downloadDatabase`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+let rqfCount = new ReqQueryFields();
+
+// cubApp.get('/year_range', (req, res, next) => {
+
+//     req.sql.from('cub')
+//     .field('MIN(cub.ano_censo)', 'start_year')
+//     .field('MAX(cub.ano_censo)', 'end_year');
+//     next();
+// }, query, response('range'));
+
+// cubApp.get('/years', (req, res, next) => {
+//     req.sql.from('cub')
+//     .field('DISTINCT cub.ano_censo', 'year');
+//     next();
+// }, query, response('years'));
+
+// cubApp.get('/months', (req, res, next) => {
+//     req.sql.from('cub')
+//     .field('DISTINCT cub.mes_censo', 'month');
+//     next();
+// }, query, response('months'));
+
+// cubApp.get('/years_months', (req, res, next) => {
+//     req.sql.from('cub')
+//     .field('DISTINCT cub.ano_censo AS "year", cub.mes_censo AS "month"');
+//     next();
+// }, query, response('years_months'));
+
+// cubApp.get('/price_type', (req, res, next) => {
+//     req.sql.from('cub')
+//     .field('DISTINCT cub.tipo_preco', 'price_type');
+//     next();
+// }, query, response('price_type'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValueToField({ 
+    name: 'state', // working
+    table: 'estado',
+    tableField: ['sigla', 'id'],
+    resultField: ['sigla_uf', 'cod_uf'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'numero_estudantes_aee'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'numero_estudantes_aee'
+    }
+}, 'filter').addValueToField({
+    name: 'city', // working
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'numero_estudantes_aee'
+    }
+}, 'filter').addValue({
+    name: 'region', // working
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'numero_estudantes_aee'
+    }
+}).addValueToField({
+    name: 'school', // working
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'numero_estudantes_aee'
+    }
+}, 'filter').addValueToField({
+    name: 'locale_id', // working
+    table: 'numero_estudantes_aee',
+    tableField: 'localidade_area_rural',
+    resultField: 'locale_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}, 'filter').addValue({
+    name: 'ethnic_group', // working
+    table: 'numero_estudantes_aee',
+    tableField: 'cor_raca_id',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca_id'
+    }
+}).addValue({
+    name: 'adm_dependency', // working
+    table: 'numero_estudantes_aee',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name:'age_range_all', // working
+    table: 'numero_estudantes_aee',
+    tableField: 'faixa_etaria_31_03',
+    resultField: 'age_range_all_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'faixa_etaria_31_03'
+    }
+  }).addValue({
+    name: 'gender', // working
+    table: 'numero_estudantes_aee',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'activity_days', // working
+    table: 'numero_estudantes_aee',
+    tableField: 'dias_atividade',
+    resultField: 'activity_days_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dias_atividade'
+    }
+}).addField({
+    name: 'special_service', // working
+    table: 'numero_estudantes_aee',
+    tableField: 'disc_atendimento_especiais',
+    resultField: 'special_service_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'disc_atendimento_especiais'
+    }
+});
+
+studentsAeeApp.get('/', rqf.parse(), (req, res, next) => {
+    req.sql.from('numero_estudantes_aee')
+    .field('numero_estudantes_aee.ano_censo')
+    .field('COUNT(distinct numero_estudantes_aee.id_aluno)', 'total')
+    .group('numero_estudantes_aee.ano_censo')
+    .order('numero_estudantes_aee.ano_censo')
+    next();
+}, rqf.build(), (req, res, next) => {
+    console.log(req.sql.toString());
+    next();
+}, query, id2str.transform(), response('studentsAee'));
+
+module.exports = studentsAeeApp;
diff --git a/src/libs/routes_v2/teacher.js b/src/libs/routes_v2/teacher.js
new file mode 100644
index 00000000..94e6d86c
--- /dev/null
+++ b/src/libs/routes_v2/teacher.js
@@ -0,0 +1,576 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const teacherApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const config = require(`${libs}/config`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]}  }).middleware;
+
+let rqf = new ReqQueryFields();
+
+teacherApp.use(cache('15 day'));
+
+// Returns a tuple of start and ending years of the complete enrollments dataset.
+teacherApp.get('/year_range', (req, res, next) => {
+    req.sql.from('docente')
+    .field('MIN(docente.ano_censo)', 'start_year')
+    .field('MAX(docente.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+teacherApp.get('/years', (req, res, next) => {
+    req.sql.from('docente').
+    field('DISTINCT docente.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+teacherApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'docente\'');
+    next();
+}, query, response('source'));
+
+teacherApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+teacherApp.get('/diff_location', (req, res, next) => {
+    req.result = [
+        {id: 0, name: "A escola não está em localidade diferenciada"},
+        {id: 1, name: "Área de assentamento"},
+        {id: 2, name: "Terra indígena"},
+        {id: 3, name: "Terra remanescente de quilombos"},
+    ];
+    next();
+}, response('diff_location'));
+
+teacherApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 8; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+teacherApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+teacherApp.get('/education_level_mod', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 12; ++i) {   
+        req.result.push({
+            id: i,
+            name: id2str.educationLevelMod(i)
+        });
+    }
+    req.result.push({
+        id: 99,
+        name: id2str.educationLevelMod(99)
+    });
+    next();
+}, response('education_level_mod'));
+
+teacherApp.get('/education_level_short', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não classificada'},
+        {id: 1, name: 'Creche'},
+        {id: 2, name: 'Pré-Escola'},
+        {id: 3, name: 'Ensino Fundamental - anos iniciais'},
+        {id: 4, name: 'Ensino Fundamental - anos finais'},
+        {id: 5, name: 'Ensino Médio'},
+        {id: 6, name: 'EJA'},
+        {id: 7, name: 'EE exclusiva'}
+    ];
+    next();
+}, response('education_level_short'));
+
+teacherApp.get('/education_type', (req, res, next) => {
+    req.sql.from('docente')
+    .field('DISTINCT nivel_tipo_formacao', 'id')
+    .order('id');
+    next();
+}, query, (req, res, next) => {
+    req.result.forEach((result) => {
+        result.name = id2str.educationType(result.id);
+    });
+    next();
+}, response('education_type'));
+
+teacherApp.get('/gender', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Masculino'},
+        {id: 2, name: 'Feminino'}
+    ];
+    next();
+}, response('gender'));
+
+
+teacherApp.get('/contract_type', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        contractType: id2str.contractType("null")
+    }];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.contractType(i)
+        });
+    }
+    next();
+}, response('contract_type'));
+
+teacherApp.get('/ethnic_group', (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <=5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ethnicGroup(i)
+        });
+    }
+    next();
+}, response('ethnic_group'));
+
+teacherApp.get('/initial_training', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <=5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.initialTraining(i)
+        });
+  }
+  next();
+}, response('initial_training'));
+
+teacherApp.get('/pos_training', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.posTraining(i)
+        });
+  }
+  next();
+}, response('pos_training'));
+
+teacherApp.get('/licentiate_degree', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 3; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.licentiateDegree(i)
+        });
+  }
+  next();
+}, response('licentiate_degree'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'adm_dependency',
+    table: 'docente',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'docente',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'contract_type',
+    table: 'docente',
+    tableField: 'tipo_contratacao',
+    resultField: 'contract_type_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'tipo_contratacao'
+    }
+}).addValue({
+    name: 'education_level_mod',
+    table: 'docente',
+    tableField: 'etapas_mod_ensino_segmento_id',
+    resultField: 'education_level_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapas_mod_ensino_segmento_id'
+    }
+}).addValue({
+    name: 'education_level_short',
+    table: 'docente',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+}).addValue({
+    name: 'education_type',
+    table: 'docente',
+    tableField: 'nivel_tipo_formacao',
+    resultField: 'education_type_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'nivel_tipo_formacao'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_regiao_id',
+        foreignTable: 'docente'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_estado_id',
+        foreignTable: 'docente'
+    }
+}).addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}, 'dims').addValueToField({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'docente'
+    }
+}, 'filter').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'docente'
+    }
+}, 'dims').addValueToField({
+    name: 'school',
+    table: 'escola',
+    tableField: 'nome_escola',
+    resultField: 'school_name',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'docente'
+    }
+}, 'filter').addValue({
+    name: 'location',
+    table: 'docente',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'diff_location',
+    table: 'docente',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'docente',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'docente',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'docente',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'gender',
+    table: 'docente',
+    tableField: 'sexo',
+    resultField: 'gender_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo'
+    }
+}).addValue({
+    name: 'ethnic_group',
+    table: 'docente',
+    tableField: 'cor_raca',
+    resultField: 'ethnic_group_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cor_raca'
+    }
+}).addValue({
+    name: 'initial_training',
+    table: 'docente',
+    tableField: 'formacao_inicial_docente',
+    resultField: 'initial_training_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'formacao_inicial_docente'
+    }
+}).addValue({
+    name: 'pos_training',
+    table: 'docente',
+    tableField: 'formacao_pos_docente',
+    resultField: 'pos_training_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'formacao_pos_docente'
+    }
+}).addValue({
+    name: 'licentiate_degree',
+    table: 'docente',
+    tableField: 'formacao_licenciatura_docente',
+    resultField: 'licentiate_degree_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'formacao_licenciatura_docente'
+    }
+});
+
+const sortYearPtid = (a, b) => {
+    if (a.year < b.year)
+        return -1
+    if (a.year > b.year)
+        return 1
+    
+    if (a.pos_training_id < b.pos_training_id)
+        return -1
+    if (a.pos_training_id > b.pos_training_id)
+        return 1
+    return 0
+}
+
+teacherApp.get('/', rqf.parse(), (req, res, next) => {
+    req.sql.field('COUNT(DISTINCT docente.id_docente)', 'total')
+    .field("'Brasil'", 'name')
+    .field('docente.ano_censo', 'year')
+    .from('docente')
+    .join('turma', null, 'docente.turma_id=turma.id AND docente.ano_censo=turma.ano_censo')
+    .group('docente.ano_censo')
+    .order('docente.ano_censo')
+    .where('(docente.tipo_docente = 1 OR docente.tipo_docente = 5) AND \
+        ((docente.tipo_turma_id >= 0 AND docente.tipo_turma_id <= 3 AND docente.tipo_turma_atendimento_id is NULL) \
+        OR ((docente.tipo_turma_atendimento_id = 1 OR docente.tipo_turma_atendimento_id = 2) AND docente.tipo_turma_id is NULL)) \
+        AND (docente.ano_censo <> 2009 or (docente.escola_estado_id <> 42 and docente.escola_estado_id <> 43) )');     // não devemos trazer SC em 2009.
+
+    // if("education_level_mod" in req.dims) {
+    //     req.hadEducationLevelMod = true;
+    //     req.sql.where('docente.etapas_mod_ensino_segmento_id < 11');
+    // }
+
+    next();
+}, rqf.build(), query, addMissing(rqf), (req, res, next) => {
+    // Função criada para preencher valores faltantes no gráfico do indicador de 
+    // formação em pós graduação do MapFOR.
+    if (req.dims.pos_training){
+        var year = req.result[0].year;
+        var posTrainingIds = req.result.map(obj => {
+            if (year == obj.year) 
+                return obj.pos_training_id
+        }).filter(num => num !== undefined)
+        
+        var missingValues = [];
+        for(let i = 1; i <= 4; ++i) {
+            if ( !posTrainingIds.includes(i) ){
+                missingValues.push(i);
+            }
+        }
+        
+        for (let curYear = 2012; curYear <= 2020; ++curYear){
+            for (let ptId of missingValues){
+                req.result.push({
+                    total:0,
+                    name:"Brasil",
+                    year:curYear,
+                    pos_training_id:ptId,
+                    pos_training_name:id2str.posTraining(ptId)
+                })
+            }
+        }
+        req.result.sort(sortYearPtid)
+        
+    }
+    next();
+}, id2str.transform(), response('teacher'));
+
+module.exports = teacherApp;
diff --git a/src/libs/routes_v2/transport.js b/src/libs/routes_v2/transport.js
new file mode 100644
index 00000000..74c1b095
--- /dev/null
+++ b/src/libs/routes_v2/transport.js
@@ -0,0 +1,400 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const transportApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+transportApp.use(cache('15 day'));
+
+transportApp.get('/year_range', (req, res, next) => {
+    req.sql.from('transporte')
+    .field('MIN(transporte.ano_censo)', 'start_year')
+    .field('MAX(transporte.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+transportApp.get('/years', (req, res, next) => {
+    req.sql.from('transporte')
+    .field('DISTINCT transporte.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+transportApp.get('/adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependency(i)
+        });
+    };
+    next();
+}, response('adm_dependency'));
+
+transportApp.get('/adm_dependency_detailed', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.admDependencyPriv(i)
+        });
+    };
+    next();
+}, response('adm_dependency_detailed'));
+
+transportApp.get('/location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: 'Urbana'},
+        {id: 2, name: 'Rural'}
+    ];
+    next();
+}, response('location'));
+
+transportApp.get('/rural_location', (req, res, next) => {
+    req.result = [
+        {id: 1, name: "Urbana"},
+        {id: 2, name: "Rural"},
+        {id: 3, name: "Rural - Área de assentamento"},
+        {id: 4, name: "Rural - Terra indígena"},
+        {id: 5, name: "Rural - Área remanescente de quilombos"},
+        {id: 6, name: "Rural - Unidade de uso sustentável"}
+    ];
+    next();
+}, response('rural_location'));
+
+transportApp.get('/education_level_mod', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não classificada'},
+        {id: 1, name: 'Creche'},
+        {id: 2, name: 'Pré-Escola'},
+        {id: 4, name: 'Ensino Fundamental - anos iniciais'},
+        {id: 5, name: 'Ensino Fundamental - anos finais'},
+        {id: 6, name: 'Ensino Médio'},
+        {id: 7, name: 'Turmas multiseriadas e multietapas'},
+        {id: 8, name: 'EJA - Ensino Fundamental'},
+        {id: 9, name: 'EJA - Ensino Médio'},
+        {id: 10, name: 'Educação Profissional'}
+    ];
+    next();
+}, response('education_level_mod'));
+
+transportApp.get('/service_type', (req, res, next) => {
+    req.result = [
+        {id: 0, name: 'Não se aplica'},
+        {id: 1, name: 'Classe hospitalar'},
+        {id: 2, name: 'Unidade de Atendimento Socioeducativo'},
+        {id: 3, name: 'Unidade Prisional'},
+        {id: 4, name: 'Atividade Complementar '},
+        {id: 5, name: 'Atendimento Educacional Especializado (AEE)'}
+    ];
+    next();
+}, response('service_type'));
+
+transportApp.get('/transportation_manager', (req, res, next) => {
+    req.result = [
+        {id: null, name: 'Não classificada'},
+        {id: 1, name: 'Estadual'},
+        {id: 2, name: 'Municipal'},
+    ];
+    next();
+}, response('transportation_manager'));
+
+transportApp.get('/source', (req, res, next) => {
+    req.sql.from('fonte')
+    .field('fonte', 'source')
+    .where('tabela = \'matricula\'');
+    next();
+}, query, response('source'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'school',
+    table: 'escola',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: ['id', 'ano_censo'],
+        foreign: ['escola_id', 'ano_censo'],
+        foreignTable: 'transporte'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'transporte'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id',
+        table: 'transporte'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'transporte'
+    }
+}).addValue({
+    name: 'state',
+    table: 'transporte',
+    tableField: ['estado_nome', 'estado_id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id',
+        table: 'transporte'
+    }
+}).addValue({
+    name: 'rural_location',
+    table: 'transporte',
+    tableField: 'localidade_area_rural',
+    resultField: 'rural_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'location',
+    table: 'transporte',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name:'adm_dependency',
+    table: 'transporte',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed',
+    table: 'transporte',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'transportation_manager',
+    table: 'transporte',
+    tableField: 'responsavel_transp',
+    resultField: 'transportation_manager_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'responsavel_transp'
+    }
+}).addValue({
+    name: 'education_level_mod',
+    table: 'transporte',
+    tableField: 'etapas_mod_ensino_segmento_id',
+    resultField: 'education_level_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapas_mod_ensino_segmento_id'
+    }
+}).addValue({
+    name: 'service_type',
+    table: 'transporte',
+    tableField: 'tipo',
+    resultField: 'service_type_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'tipo'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'transporte',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'transporte',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'year',
+    table: 'transporte',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+});
+
+transportApp.get('/', rqf.parse(), (req, res, next) => {
+    req.dims.year = true;
+    req.sql
+        .field('sum(transporte.total)', 'total')
+        .field("'Brasil'", 'name')
+        .from('transporte')
+        .where('transporte.transporte_id=0')
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    req.total = req.result;
+    req.resetSql();
+    next();
+}, rqf.parse(), (req, res, next) => {
+    req.dims.year = true;
+    req.sql
+        .field('sum(transporte.total)', 'total')
+        .field("'Brasil'", 'name')
+        .from('transporte')
+        .where('transporte.transporte_id=1')
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    req.public_total = req.result;
+    req.resetSql();
+    next();
+
+}, rqf.parse(), (req, res, next) => {
+    req.dims.year = true;
+    req.sql
+        .field('sum(transporte.total)', 'total')
+        .field("'Brasil'", 'name')
+        .field('transporte.transporte_id', 'id')
+        .from('transporte')
+        .where('transporte.transporte_id>0')
+        .group('transporte.transporte_id')
+        .order('transporte.transporte_id')
+    next();
+}, rqf.build(), query, id2str.transform(), (req, res, next) => {
+    let transports = req.result;
+
+    let results = [];
+    let obj = {};
+    for (let i = 1; i < 13; i++) {
+        obj[id2str.transport(i)] = [];
+    }
+
+    let i = 0
+    while (i < transports.length) {
+        let result = [];
+        let j = 0;
+        let transport = transports[i];
+        let totalArray = (transport.id  == 1) ? req.total : req.public_total;
+        let match;
+        obj[id2str.transport(transport.id)] = result;
+        while (j < totalArray.length && i < transports.length) {
+            transport = transports[i];
+            let transportTotal = totalArray[j];
+
+            let currentTransport = {};
+            delete transport.id;
+            match = true;
+            Object.keys(transport).forEach(function(key) {
+                currentTransport[key] = transportTotal[key];
+                if (key != 'total' && transport[key] != transportTotal[key]) {
+                    match = false;
+                    return;
+                }
+            })
+
+            if (match) {
+                currentTransport.partial = (match) ? transport.total : 0;
+                currentTransport.percentage = (match) ? transport.total/transportTotal.total * 100 : 0;
+                result.push(currentTransport);
+                i++;
+                j++;
+            }
+            else
+                j++;
+        }
+    }
+    results.push(obj)
+    req.result = results;
+
+    next();
+}, response('transports'));
+
+module.exports = transportApp;
diff --git a/src/libs/routes_v2/university.js b/src/libs/routes_v2/university.js
new file mode 100644
index 00000000..be7fe771
--- /dev/null
+++ b/src/libs/routes_v2/university.js
@@ -0,0 +1,347 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const universityApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+let rqf = new ReqQueryFields();
+
+let rqfCount = new ReqQueryFields();
+
+universityApp.use(cache('15 day'));
+
+universityApp.get('/upper_adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 7; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.upperAdmDependency(i)
+        });
+    };
+    next();
+}, response('upper_adm_dependency'));
+
+universityApp.get('/years', (req, res, next) => {
+    req.sql.from('ies_ens_superior')
+    .field('DISTINCT ies_ens_superior.ano_censo', 'year')
+    next();
+}, query, response('years'));
+
+universityApp.get('/year_range', (req, res, next) => {
+    req.sql.from('ies_ens_superior')
+    .field('MIN(ies_ens_superior.ano_censo)', 'start_year')
+    .field('MAX(ies_ens_superior.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+universityApp.get('/academic_organization', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.academicOrganization(i)
+        });
+    };
+    next();
+}, response('academic_organization'));
+
+universityApp.get('/capital', (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.booleanVariable(i)
+        });
+    };
+    next();
+}, response('capital'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'id',
+    table: 'ies_ens_superior',
+    tableField: 'cod_ies',
+    resultField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ies'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_municipio_ies',
+        table: 'ies_ens_superior'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio_ies',
+        foreignTable: 'ies_ens_superior'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_uf_ies',
+        table: 'ies_ens_superior'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_uf_ies',
+        foreignTable: 'ies_ens_superior'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'id'
+    },
+    join: {
+        primary: 'nome',
+        foreign: 'nome_regiao_ies',
+        foreignTable: 'ies_ens_superior'
+    }
+}).addValue({
+    name: 'year',
+    table: 'ies_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo',
+        table: 'ies_ens_superior'
+    }
+}).addField({
+    name: 'search',
+    field: true,
+    where: true
+}).addValueToField({
+    name: 'city_name',
+    table: 'municipio',
+    tableField: 'nome',
+    resultField: 'city_name',
+    dontGroup: true,
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'nome'
+    },
+    join: {
+      primary: 'id',
+      foreign: 'cod_municipio_ies',
+      foreignTable: 'ies_ens_superior'
+    }
+}, 'search')
+.addValueToField({
+    name: 'state_name',
+    table: 'estado',
+    tableField: 'nome',
+    resultField: 'state_name',
+    dontGroup: true,
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'sigla'
+    },
+    join: {
+      primary: 'id',
+      foreign: 'cod_uf_ies',
+      foreignTable: 'ies_ens_superior'
+    }
+}, 'search');
+
+
+rqfCount.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_municipio_ies',
+        table: 'ies_ens_superior'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio_ies',
+        foreignTable: 'ies_ens_superior'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'id'
+    },
+    join: {
+        primary: 'nome',
+        foreign: 'nome_regiao_ies',
+        foreignTable: 'ies_ens_superior'
+    }
+
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_uf_ies',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_uf_ies',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'upper_adm_dependency',
+    table: 'ies_ens_superior',
+    tableField: 'par_categoria_administrativa',
+    resultField: 'upper_adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'ies_ens_superior',
+        field: 'cod_categoria_administrativa'
+    }
+}).addValue({
+    name: 'academic_organization',
+    table: 'ies_ens_superior',
+    tableField: 'cod_organizacao_academica',
+    resultField: 'academic_organization_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'ies_ens_superior',
+        field: 'cod_organizacao_academica'
+    }
+}).addValue({
+    name: 'capital',
+    table: 'ies_ens_superior',
+    tableField: 'tfd_capital_ies',
+    resultField: 'capital_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'ies_ens_superior',
+        field: 'capital_ies'
+    }
+});
+
+universityApp.get('/', rqf.parse(),  rqf.build(), (req, res, next) => {
+    req.sql.from('ies_ens_superior')
+        .field('ies_ens_superior.cod_ies', 'id')
+        .field('ies_ens_superior.ano_censo', 'year')
+        .field('ies_ens_superior.nome_ies', 'name')
+        .field('ies_ens_superior.cod_uf_ies', 'state_id')
+        .field('ies_ens_superior.cod_municipio_ies', 'city_id')
+        .field('ies_ens_superior.cod_uf_ies/10', 'region_id');
+    next();
+
+}, query, response('university'));
+
+universityApp.get('/count', rqfCount.parse(), (req, res, next) => {
+    req.sql.field('COUNT(*)', 'total')
+    .field("'Brasil'", 'name')
+    .field('ies_ens_superior.ano_censo', 'year')
+    .from('ies_ens_superior')
+    .group('ies_ens_superior.ano_censo')
+    .order('ies_ens_superior.ano_censo')
+
+    next();
+}, rqfCount.build(), query, addMissing(rqfCount), id2str.transform(), response('university'));
+
+module.exports = universityApp;
diff --git a/src/libs/routes_v2/universityEnrollment.js b/src/libs/routes_v2/universityEnrollment.js
new file mode 100644
index 00000000..8df5aded
--- /dev/null
+++ b/src/libs/routes_v2/universityEnrollment.js
@@ -0,0 +1,848 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const universityEnrollmentApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+universityEnrollmentApp.get('/years', (req, res, next) => {
+    req.sql.from('aluno_ens_superior')
+    .field('DISTINCT aluno_ens_superior.ano_censo', 'year')
+		.where('aluno_ens_superior.ano_censo > 2010');
+    next();
+}, query, response('years'));
+
+universityEnrollmentApp.get('/year_range', (req, res, next) => {
+    req.sql.from('aluno_ens_superior')
+    .field('MIN(aluno_ens_superior.ano_censo)', 'start_year')
+    .field('MAX(aluno_ens_superior.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+universityEnrollmentApp.get('/academic_organization', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.academicOrganization(i)
+        });
+    };
+    next();
+}, response('academic_organization'));
+
+universityEnrollmentApp.get('/upper_adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 7; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.upperAdmDependency(i)
+        });
+    };
+    next();
+}, response('upper_adm_dependency'));
+
+universityEnrollmentApp.get('/ocde_geral', (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <= 8; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ocdeGeral(i)
+        });
+    };
+    next();
+}, response('ocde_geral'));
+
+
+universityEnrollmentApp.get('/finish', (req, res, next) => {
+    req.result = []
+    for (let i = 0; i <= 1; ++i){
+        req.result.push({
+            id: i,
+            name: id2str.finishUniversity(i)
+        })
+    }
+    next();
+
+}, response('finish'));
+
+universityEnrollmentApp.get('/ocde_specific', (req, res, next) => {
+    req.result = [];
+    const defaultCase = null;
+    for(let i = 1; i <= 86; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.ocdeSpecific(i)
+        };
+        if (obj.name !== id2str.ocdeSpecific(defaultCase)){
+            req.result.push(obj);
+        }
+    };
+    req.result.push({
+        id: defaultCase,
+        name: id2str.ocdeSpecific(defaultCase)
+    });
+    next();
+}, response('ocde_specific'));
+
+universityEnrollmentApp.get('/ocde_detailed', (req, res, next) => {
+    req.result = [];
+    const defaultCase = null;
+    for(let i = 142; i <= 863; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.ocdeDetailed(i)
+        };
+        if (obj.name !== id2str.ocdeDetailed(defaultCase)){
+            req.result.push(obj);
+        }
+    };
+    req.result.push({
+        id: defaultCase,
+        name: id2str.ocdeDetailed(defaultCase)
+    });
+    next();
+}, response('ocde_detailed'));
+
+universityEnrollmentApp.get('/cine_geral', (req, res, next) => {
+    req.result = [];
+    for(let i = 0; i <= 10; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.cineGeral(i)
+        });
+    };
+    next();
+}, response('cine_geral'));
+
+universityEnrollmentApp.get('/cine_specific', (req, res, next) => {
+    req.result = [];
+    const defaultCase = null;
+    for(let i = 1; i <= 104; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.cineSpecific(i)
+        };
+        if (obj.name !== id2str.cineSpecific(defaultCase)){
+            req.result.push(obj);
+        }
+    };
+    req.result.push({
+        id: defaultCase,
+        name: id2str.cineSpecific(defaultCase)
+    });
+    next();
+}, response('cine_specific'));
+
+universityEnrollmentApp.get('/cine_detailed', (req, res, next) => {
+    req.result = [];
+    const defaultCase = null;
+    for(let i = 11; i <= 1041; ++i) {
+        let obj = {
+            id: i,
+            name: id2str.cineDetailed(i)
+        };
+        if (obj.name !== id2str.cineDetailed(defaultCase)){
+            req.result.push(obj);
+        }
+    };
+    req.result.push({
+        id: defaultCase,
+        name: id2str.cineDetailed(defaultCase)
+    });
+    next();
+}, response('cine_detailed'));
+
+universityEnrollmentApp.get('/upper_turn', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.upperTurn("null")
+    }];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.upperTurn(i)
+        });
+    };
+    next();
+}, response('upper_turn'));
+
+universityEnrollmentApp.get('/student_deficiency', (req, res, next) => {
+    req.result = [{
+        id: 9,
+        name: id2str.studentDeficiency(9)
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.studentDeficiency(i)
+        });
+    };
+    next();
+}, response('student_deficiency'));
+
+universityEnrollmentApp.get('/ethnic_group_ies', (req, res, next) => {
+    req.result = [{
+        id: 9,
+        name: id2str.ethnicGroupIES(9)
+    }];
+    for(let i = 0; i <=5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ethnicGroupIES(i)
+        });
+    }
+    next();
+}, response('ethnic_group_ies'));
+
+universityEnrollmentApp.get('/school_type', (req, res, next) => {
+    req.result = [{
+        id: 9,
+        name: id2str.schoolType(9)
+    }];
+    for(let i = 1; i <= 2; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.schoolType(i)
+        });
+    };
+    next();
+}, response('school_type'));
+
+universityEnrollmentApp.get('/university', (req, res, next) => {
+    req.sql.from('aluno_ens_superior')
+    .field('DISTINCT aluno_ens_superior.nome_ies', 'nome')
+    .field('aluno_ens_superior.cod_ies', 'cod')
+    next();
+}, query, response('university'));
+
+universityEnrollmentApp.get('/academic_level', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.academicLevel(i)
+        });
+    };
+    next();
+}, response('academic_level'));
+
+universityEnrollmentApp.get('/gender_ies', function (req, res, next) {
+    req.result = [];
+    for (var i = 1; i <= 2; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.genderIES(i)
+        });
+    };
+    next();
+}, response('gender_ies'));
+
+universityEnrollmentApp.get('/upper_education_mod', function (req, res, next) {
+    req.result = [];
+    for (var i = 1; i <= 3; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.upperEducationMod(i)
+        });
+    };
+    next();
+}, response('upper_education_mod'));
+
+universityEnrollmentApp.get('/age_student_code', function (req, res, next) {
+    req.result = [];
+    for (var i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ageStudentCode(i)
+        });
+    };
+    next();
+}, response('age_student_code'));
+
+
+universityEnrollmentApp.get('/enter_situation/student_enter_situation', function (req, res, next) {
+    req.result = [];
+    for (var i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.enterSituation(i)
+        });
+    };
+    next();
+}, response('student_enter_situation'));
+
+universityEnrollmentApp.get('/enrollment_situation/student_enter_situation', function (req, res, next) {
+    req.result = [];
+    for (var i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.enterSituation(i)
+        });
+    };
+    next();
+}, response('student_enter_situation'));
+
+
+universityEnrollmentApp.get('/enter_situation/student_enrollment_situation', function (req, res, next) {
+    req.result = [];
+    for (var i = 1; i <= 3; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.enrollmentSituation(i)
+        });
+    };
+    next();
+}, response('student_enrollment_situation'));
+
+universityEnrollmentApp.get('/university', (req, res, next) => {
+    req.sql.from('aluno_ens_superior')
+    .field('DISTINCT aluno_ens_superior.nome_ies', 'nome')
+    .field('aluno_ens_superior.cod_ies', 'cod')
+    next();
+}, query, response('university'));
+
+universityEnrollmentApp.get('/localoffer', (req, res, next) => {
+    req.sql.from('localoferta_ens_superior', 'l')
+    .field('DISTINCT l.nome', 'localoffer_name')
+    .field('l.cod_local_oferta', 'localoffer_id');
+    next();
+}, query, response('localoffer'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localoferta_cod_municipio',
+        table: 'localoferta_ens_superior_matricula'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'localoferta_cod_municipio',
+        foreignTable: 'localoferta_ens_superior_matricula'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localoferta_cod_uf',
+        table: 'localoferta_ens_superior_matricula'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'localoferta_cod_uf',
+        foreignTable: 'localoferta_ens_superior_matricula'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localoferta_cod_regiao',
+        table: 'localoferta_ens_superior_matricula'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'localoferta_cod_regiao',
+        foreignTable: 'localoferta_ens_superior_matricula'
+    }
+}).addValue({
+    name: 'localoffer',
+    table: 'localoferta_ens_superior_matricula',
+    tableField: ['cod_local_oferta', 'localoferta_nome'],
+    resultField: ['localoffer_id', 'localoffer_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_local_oferta'
+    }
+}).addValue({
+    name: 'campi',
+    table: 'localoferta_ens_superior_matricula',
+    tableField: ['cod_local_oferta', 'localoferta_nome'],
+    resultField: ['campi_id', 'campi_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_local_oferta'
+    }
+}).addValue({
+    name: 'university',
+    table: '@',
+    tableField: ['cod_ies', 'nome_ies'],
+    resultField: ['university_id', 'university_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ies'
+    }
+}).addValue({
+    name: 'universityLocalOffer',
+    table: '@',
+    tableField: ['cod_ies', 'nome_ies'],
+    resultField: ['university_id', 'university_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ies'
+    }
+}).addValue({
+    name:'upper_adm_dependency',
+    table: '@',
+    tableField: 'par_categoria_administrativa',
+    resultField: 'upper_adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_categoria_administrativa'
+    }
+}).addValue({
+    name:'academic_organization',
+    table: '@',
+    tableField: 'cod_organizacao_academica',
+    resultField: 'academic_organization_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_organizacao_academica'
+    }
+}).addValue({
+    name:'ocde_specific',
+    table: '@',
+    tableField: ['par_cod_ocde_area_especifica'],
+    resultField: ['ocde_specific_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_cod_ocde_area_especifica'
+    }
+}).addValue({
+    name:'ocde_geral',
+    table: '@',
+    tableField: ['par_cod_ocde_area_geral'],
+    resultField: ['ocde_geral_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_cod_ocde_area_geral'
+    }
+}).addValue({
+    name:'ocde_detailed',
+    table: '@',
+    tableField: ['par_cod_ocde_area_detalhada'],
+    resultField: ['ocde_detailed_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_cod_ocde_area_detalhada'
+    }
+}).addValue({
+    name:'cine_specific',
+    table: '@',
+    tableField: ['cod_cine_area_especifica', 'nome_cine_area_especifica'],
+    resultField: ['cine_specific_id', 'cine_specific_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_cine_area_especifica'
+    }
+}).addValue({
+    name:'cine_geral',
+    table: '@',
+    tableField: ['cod_cine_area_geral', 'nome_cine_area_geral'],
+    resultField: ['cine_geral_id', 'cine_geral_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_cine_area_geral'
+    }
+}).addValue({
+    name:'cine_detailed',
+    table: '@',
+    tableField: ['cod_cine_area_detalhada', 'nome_cine_area_detalhada'],
+    resultField: ['cine_detailed_id', 'cine_detailed_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_cine_area_detalhada'
+    }
+}).addValue({
+    name:'academic_level',
+    table: '@',
+    tableField: 'cod_grau_academico',
+    resultField: 'academic_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_grau_academico'
+    }
+}).addValue({
+    name:'upper_education_mod',
+    table: '@',
+    tableField: 'cod_modalidade_ensino',
+    resultField: 'upper_education_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_modalidade_ensino'
+    }
+}).addValue({
+    name:'is_free',
+    table: '@',
+    tableField: 'gratuito',
+    resultField: 'is_free_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'gratuito'
+    }
+}).addValue({
+    name:'night_time',
+    table: '@',
+    tableField: 'noturno_curso_t',
+    resultField: 'night_time_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'noturno_curso_t'
+    }
+}).addValue({
+    name:'situation',
+    table: '@',
+    tableField: 'cod_situacao_curso',
+    resultField: 'situacao_curso_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_situacao_curso'
+    }
+}).addValue({
+    name:'finish',
+    table: '@',
+    tableField: 'concluinte',
+    resultField: 'finish_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'concluinte'
+    }
+}).addValue({
+    name:'year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'age_student_code',
+    table: '@',
+    tableField: 'idade_aluno_codigo',
+    resultField: 'age_student_code_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'idade_aluno_codigo'
+    }
+}).addValue({
+    name:'upper_turn',
+    table: '@',
+    tableField: 'cod_turno_aluno',
+    resultField: 'upper_turn_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_turno_aluno'
+    }
+}).addValue({
+    name:'ethnic_group_ies',
+    table: '@',
+    tableField: 'par_cod_cor_raca_aluno',
+    resultField: 'ethnic_group_ies_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_cod_cor_raca_aluno'
+    }
+}).addValue({
+    name:'student_deficiency',
+    table: '@',
+    tableField: 'par_aluno_deficiencia_transtorno_superdotacao',
+    resultField: 'student_deficiency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_aluno_deficiencia_transtorno_superdotacao'
+    }
+}).addValue({
+    name:'school_type',
+    table: '@',
+    tableField: 'par_tipo_escola_ensino_medio',
+    resultField: 'school_type_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_tipo_escola_ensino_medio'
+    }
+}).addValue({
+    name: 'gender_ies',
+    table: '@',
+    tableField: 'par_genero_aluno',
+    resultField: 'gender_ies_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_genero_aluno'
+    }
+}).addValue({
+    name: 'mesoregion',
+    table: 'municipio',
+    tableField: ['nome_mesorregiao', 'mesorregiao_id'],
+    resultField: ['mesoregion_name', 'mesoregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'mesorregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'localoferta_cod_municipio',
+        foreignTable: 'localoferta_ens_superior_matricula'
+    }
+}).addValue({
+    name: 'microregion',
+    table: 'municipio',
+    tableField: ['nome_microrregiao', 'microrregiao_id'],
+    resultField: ['microregion_name', 'microregion_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'microrregiao_id',
+        table: 'municipio'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'localoferta_cod_municipio',
+        foreignTable: 'localoferta_ens_superior_matricula'
+    }
+}).addValue({
+    name: 'course',
+    table: 'curso_ens_superior',
+    tableField: 'nome_curso',
+    resultField: 'course_name',
+    where:{
+        relation: '=',
+        type: 'string',
+        table:  'curso_ens_superior',
+        field: 'nome_curso'
+    },
+    join:{
+        primary: ['ano_censo', 'cod_curso'],
+        foreign: ['ano_censo', 'cod_curso'],
+        foreignTable: 'localoferta_ens_superior_matricula'
+    }
+});
+
+universityEnrollmentApp.get('/', rqf.parse(), (req, res, next) => {
+    if ("localoffer" in req.dims) {
+        if ("university" in req.dims || "universityLocalOffer" in req.dims) {
+            req.sql.from('localoferta_ens_superior_matricula')
+                .field('curso_ens_superior.ano_censo', 'year')
+                .field('COUNT(localoferta_ens_superior.cod_local_oferta)', 'total')
+                .group('localoferta_ens_superior_matricula.ano_censo')
+                .where('localoferta_ens_superior_matricula.cod_aluno_situacao = 2 OR localoferta_ens_superior_matricula.cod_aluno_situacao = 6 OR localoferta_ens_superior_matricula.matriculado = 1')
+                .where('localoferta_ens_superior_matricula.cod_nivel_academico = 1')
+                .order('localoferta_ens_superior_matricula.ano_censo')
+                .order('localoferta_ens_superior.cod_local_oferta');
+        } else {
+            req.sql.from('localoferta_ens_superior_matricula')
+                .field('localoferta_ens_superior_matricula.ano_censo', 'year')
+                .field('COUNT(*)', 'total')
+                .field('localoferta_ens_superior_matricula.cod_ies', 'university_id')
+                .field('localoferta_ens_superior_matricula.nome_ies', 'university_name')
+                .where('localoferta_ens_superior_matricula.cod_aluno_situacao = 2 OR localoferta_ens_superior_matricula.cod_aluno_situacao = 6 OR localoferta_ens_superior_matricula.matriculado = 1')
+                .where('localoferta_ens_superior_matricula.cod_nivel_academico = 1')
+                .group('localoferta_ens_superior_matricula.ano_censo')
+                .group('localoferta_ens_superior_matricula.cod_ies')
+                .group('localoferta_ens_superior_matricula.nome_ies')
+                .order('localoferta_ens_superior_matricula.ano_censo')
+                .order('localoferta_ens_superior_matricula.cod_local_oferta');
+        }
+    } else if (("state" in req.dims) || ("city" in req.dims) || ("region" in req.dims) || ("mesoregion" in req.dims) || ("microregion" in req.dims) ||
+        ("state" in req.filter) || ("city" in req.filter) || ("region" in req.filter) || ("mesoregion" in req.filter) || ("microregion" in req.filter)) {
+        req.sql.from('localoferta_ens_superior_matricula')
+        .field('DISTINCT COUNT(*)', 'total')
+        .field("'Brasil'", 'name')
+        .field('localoferta_ens_superior_matricula.ano_censo', 'year')
+        .where('localoferta_ens_superior_matricula.cod_aluno_situacao = 2 OR localoferta_ens_superior_matricula.cod_aluno_situacao = 6 OR localoferta_ens_superior_matricula.matriculado = 1')
+        .where('localoferta_ens_superior_matricula.cod_nivel_academico = 1')
+        .group('localoferta_ens_superior_matricula.ano_censo')
+        .order('localoferta_ens_superior_matricula.ano_censo')
+    } else if ("university" in req.dims || "universityLocalOffer" in req.dims) {
+        req.sql.from('aluno_ens_superior')
+        .field('COUNT(*)', 'total')
+        .field("'Brasil'", 'name')
+        .field('aluno_ens_superior.ano_censo', 'year')
+        .group('aluno_ens_superior.cod_ies')
+        .group('aluno_ens_superior.ano_censo')
+        .where('aluno_ens_superior.cod_aluno_situacao = 2 OR aluno_ens_superior.cod_aluno_situacao = 6 OR aluno_ens_superior.matriculado = 1')
+        .where('aluno_ens_superior.cod_nivel_academico = 1')
+        .order('aluno_ens_superior.cod_ies')
+        .order('aluno_ens_superior.ano_censo')
+    } else {
+        req.sql.from('localoferta_ens_superior_matricula')
+        .field('COUNT(*)', 'total')
+        .field("'Brasil'", 'name')
+        .field('localoferta_ens_superior_matricula.ano_censo', 'year')
+        .where('localoferta_ens_superior_matricula.cod_aluno_situacao = 2 OR localoferta_ens_superior_matricula.cod_aluno_situacao = 6 OR localoferta_ens_superior_matricula.matriculado = 1')
+        .where('localoferta_ens_superior_matricula.cod_nivel_academico = 1')
+        .group('localoferta_ens_superior_matricula.ano_censo')
+        .order('localoferta_ens_superior_matricula.ano_censo')
+    }
+    next();
+}, rqf.build(), query, (req, res, next) =>{ console.log(req.sql.toString()); next()}, id2str.transform(), addMissing(rqf), (req, res, next) => {
+    if ('course' in req.dims){
+        var total_course = req.result.reduce((total, cur) => {return total += cur.total}, 0)
+        for (var course of req.result){
+            course.percentage = Number((( course.total / total_course ) * 100).toFixed(2))
+        }
+    }
+    next();
+}, response('universityEnrollment'));
+
+universityEnrollmentApp.get('/enter_situation', rqf.parse(), (req, res, next) => {
+    req.sql.from('localoferta_ens_superior_matricula')
+    .field('SUM(CASE WHEN localoferta_ens_superior_matricula.cod_aluno_situacao=2 AND localoferta_ens_superior_matricula.ingressante=1 THEN 1 ELSE 0 END)', 'cursando')
+    .field('SUM(CASE WHEN localoferta_ens_superior_matricula.cod_aluno_situacao=6 AND localoferta_ens_superior_matricula.ingressante=1 THEN 1 ELSE 0 END)', 'concluinte')
+    .field('SUM(CASE WHEN (localoferta_ens_superior_matricula.cod_aluno_situacao=4 OR localoferta_ens_superior_matricula.cod_aluno_situacao=5 OR localoferta_ens_superior_matricula.cod_aluno_situacao=7) AND localoferta_ens_superior_matricula.ingressante=1 THEN 1 ELSE 0 END)', 'evadido')
+    .field('SUM(CASE WHEN localoferta_ens_superior_matricula.cod_aluno_situacao=3 AND localoferta_ens_superior_matricula.ingressante=1 THEN 1 ELSE 0 END)', 'trancado')
+    .field('COUNT(*)', 'total')
+    .field("'Brasil'", 'name')
+    .field('localoferta_ens_superior_matricula.ano_censo', 'year')
+    .where('localoferta_ens_superior_matricula.cod_nivel_academico=1')
+    .where('localoferta_ens_superior_matricula.cod_grau_academico=2 OR localoferta_ens_superior_matricula.cod_grau_academico=4')
+    .group('localoferta_ens_superior_matricula.ano_censo')
+    .order('localoferta_ens_superior_matricula.ano_censo')
+    next()
+}, rqf.build(), query, (req, res, next) => {
+    for (var res of req.result){
+        res.cursando = Number(res.cursando);
+        res.concluinte = Number(res.concluinte);
+        res.evadido = Number(res.evadido);
+        res.trancado = Number(res.trancado);
+        res.total = res.cursando + res.concluinte + res.evadido + res.trancado
+        res.taxa_evasao = Number( ((res.evadido/res.total) * 100).toFixed(2) )
+    }
+    next();
+}, id2str.transform(), response('enterSituation'));
+
+universityEnrollmentApp.get('/enrollment_situation', rqf.parse(), (req, res, next) => {
+    req.sql.from('localoferta_ens_superior_matricula')
+    .field('SUM(CASE WHEN localoferta_ens_superior_matricula.cod_aluno_situacao=2 THEN 1 ELSE 0 END)', 'cursando')
+    .field('SUM(CASE WHEN localoferta_ens_superior_matricula.cod_aluno_situacao=6 THEN 1 ELSE 0 END)', 'concluinte')
+    .field('SUM(CASE WHEN (localoferta_ens_superior_matricula.cod_aluno_situacao=4 OR localoferta_ens_superior_matricula.cod_aluno_situacao=5 OR localoferta_ens_superior_matricula.cod_aluno_situacao=7) THEN 1 ELSE 0 END)', 'evadido')
+    .field('SUM(CASE WHEN localoferta_ens_superior_matricula.cod_aluno_situacao=3 THEN 1 ELSE 0 END)', 'trancado')
+    .field('localoferta_ens_superior_matricula.ano_censo', 'year')
+    .field("'Brasil'", 'name')
+    .where('localoferta_ens_superior_matricula.cod_nivel_academico=1')
+    .where('localoferta_ens_superior_matricula.cod_grau_academico=2 OR localoferta_ens_superior_matricula.cod_grau_academico=4')
+    .group('localoferta_ens_superior_matricula.ano_censo')
+    .order('localoferta_ens_superior_matricula.ano_censo')
+    next()
+}, rqf.build(),query, (req, res, next) => {
+    for (var res of req.result){
+        res.cursando = Number(res.cursando);
+        res.concluinte = Number(res.concluinte);
+        res.evadido = Number(res.evadido);
+        res.trancado = Number(res.trancado);
+        res.total = res.cursando + res.concluinte + res.evadido + res.trancado
+    }
+    
+    next();
+}, id2str.transform(), response('enrollmentSituation'));
+
+
+
+module.exports = universityEnrollmentApp;
diff --git a/src/libs/routes_v2/universityLocalOffer.js b/src/libs/routes_v2/universityLocalOffer.js
new file mode 100644
index 00000000..535094c4
--- /dev/null
+++ b/src/libs/routes_v2/universityLocalOffer.js
@@ -0,0 +1,170 @@
+''/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const universityLocalOfferApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const request = require(`request`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+let rqf = new ReqQueryFields();
+
+universityLocalOfferApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_municipio',
+        table: 'localoferta_ens_superior'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio',
+        foreignTable: 'localoferta_ens_superior'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: 'LIKE',
+        type: 'string',
+        field: 'id'
+    },
+    join: {
+        primary: 'nome',
+        foreign: 'nome_regiao_ies',
+        foreignTable: 'localoferta_ens_superior'
+    }
+
+}).addValue({
+    name: 'min_year',
+    table: 'localoferta_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: 'localoferta_ens_superior',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'localoferta_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: 'localoferta_ens_superior',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_uf',
+        table: 'localoferta_ens_superior'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_uf',
+        foreignTable: 'localoferta_ens_superior'
+    }
+}).addValueToField({
+    name: 'university',
+    table: 'localoferta_ens_superior',
+    tableField: 'cod_ies',
+    resultField: 'university_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ies',
+        table: 'localoferta_ens_superior'
+    }
+}, 'filter').addValue({
+    name: 'year',
+    table: 'localoferta_ens_superior',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'localoferta_ens_superior',
+        field: 'ano_censo'
+    }
+});
+
+
+universityLocalOfferApp.get('/', rqf.parse(),  rqf.build(), (req, res, next) => {
+    req.sql.from('localoferta_ens_superior')
+        .field('distinct localoferta_ens_superior.cod_ies', 'id')
+        .field('localoferta_ens_superior.cod_local_oferta', 'localoffer_id')
+        .field('localoferta_ens_superior.ano_censo', 'year')
+        .field('ies_ens_superior.nome_ies', 'name')
+        .field('localoferta_ens_superior.nome', 'localoffer_name')
+        .field('localoferta_ens_superior.cod_uf', 'state_id')
+        .field('localoferta_ens_superior.cod_municipio', 'city_id')
+        .field('localoferta_ens_superior.cod_regiao', 'region_id')
+        .join('ies_ens_superior', null, 'localoferta_ens_superior.cod_ies=ies_ens_superior.cod_ies AND localoferta_ens_superior.ano_censo=ies_ens_superior.ano_censo')
+        .where('localoferta_ens_superior.nome IS NOT NULL AND ies_ens_superior.nome_ies IS NOT NULL');
+    next();
+
+}, query, response('universityLocalOfferApp'));
+
+module.exports = universityLocalOfferApp;
diff --git a/src/libs/routes_v2/universityTeacher.js b/src/libs/routes_v2/universityTeacher.js
new file mode 100644
index 00000000..f65a29b2
--- /dev/null
+++ b/src/libs/routes_v2/universityTeacher.js
@@ -0,0 +1,517 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const teacherEnrollmentApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+const addMissing = require(`${libs}/middlewares/addMissing`);
+
+const config = require(`${libs}/config`);
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+teacherEnrollmentApp.get('/years', (req, res, next) => {
+    req.sql.from('docente_ens_superior')
+    .field('DISTINCT docente_ens_superior.ano_censo', 'year');
+    next();
+}, query, response('years'));
+
+teacherEnrollmentApp.get('/year_range', (req, res, next) => {
+    req.sql.from('docente_ens_superior')
+    .field('MIN(docente_ens_superior.ano_censo)', 'start_year')
+    .field('MAX(docente_ens_superior.ano_censo)', 'end_year');
+    next();
+}, query, response('range'));
+
+teacherEnrollmentApp.get('/academic_organization', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.academicOrganization(i)
+        });
+    };
+    next();
+}, response('academic_organization'));
+
+teacherEnrollmentApp.get('/upper_adm_dependency', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 7; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.upperAdmDependency(i)
+        });
+    };
+    next();
+}, response('upper_adm_dependency'));
+
+teacherEnrollmentApp.get('/teacher_situation', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 6; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.teacherSituation(i)
+        });
+    };
+    next();
+}, response('teacher_situation'));
+
+teacherEnrollmentApp.get('/work_regime', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.workRegime("null")
+    }];
+    for(let i = 1; i <= 4; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.workRegime(i)
+        });
+    };
+    next();
+}, response('work_regime'));
+
+teacherEnrollmentApp.get('/substitute', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.booleanVariable("null")
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.booleanVariable(i)
+        });
+    };
+    next();
+}, response('substitute'));
+
+teacherEnrollmentApp.get('/visitor', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.booleanVariable("null")
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.booleanVariable(i)
+        });
+    };
+    next();
+}, response('visitor'));
+
+teacherEnrollmentApp.get('/ead_teacher', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.booleanVariable("null")
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.booleanVariable(i)
+        });
+    };
+    next();
+}, response('ead_teacher'));
+
+teacherEnrollmentApp.get('/graduation_presential', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.booleanVariable("null")
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.booleanVariable(i)
+        });
+    };
+    next();
+}, response('graduation_presential'));
+
+teacherEnrollmentApp.get('/postgraduate_ead_teacher', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.booleanVariable("null")
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.booleanVariable(i)
+        });
+    };
+    next();
+}, response('postgraduate_ead_teacher'));
+
+teacherEnrollmentApp.get('/postgraduate_presential_teacher', (req, res, next) => {
+    req.result = [{
+        id: "null",
+        name: id2str.booleanVariable("null")
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.booleanVariable(i)
+        });
+    };
+    next();
+}, response('postgraduate_presential_teacher'));
+
+teacherEnrollmentApp.get('/deficiency', (req, res, next) => {
+    req.result = [{
+        id: 9,
+        name: id2str.studentDeficiency(9)
+    }];
+    for(let i = 0; i <= 1; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.studentDeficiency(i)
+        });
+    };
+    next();
+}, response('deficiency'));
+
+teacherEnrollmentApp.get('/ethnic_group_teacher_ies', (req, res, next) => {
+    req.result = [{
+        id: 9,
+        name: id2str.ethnicGroupTeacherIES(9)
+    }];
+    for(let i = 0; i <= 5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.ethnicGroupTeacherIES(i)
+        });
+    };
+    next();
+}, response('ethnic_group_teacher_ies'));
+
+teacherEnrollmentApp.get('/teacher_schooling', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 5; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.teacherSchooling(i)
+        });
+    };
+    next();
+}, response('teacher_schooling'));
+
+teacherEnrollmentApp.get('/gender_ies', (req, res, next) => {
+    req.result = [];
+    for(let i = 1; i <= 2; ++i) {
+        req.result.push({
+            id: i,
+            name: id2str.genderIES(i)
+        });
+    };
+    next();
+}, response('gender_ies'));
+
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'min_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        table: '@',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_uf_ies',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_uf_ies',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_municipio_ies',
+        table: '@'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_municipio_ies',
+        foreignTable: '@'
+    }
+}).addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'cod_regiao_ies',
+        foreignTable: 'docente_ens_superior'
+    }
+}).addValue({
+    name: 'university',
+    table: 'docente_ens_superior',
+    tableField: ['cod_ies', 'nome_ies'],
+    resultField: ['university_id', 'university_name'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_ies'
+    }
+}).addValue({
+    name: 'upper_adm_dependency',
+    table: 'docente_ens_superior',
+    tableField: 'par_categoria_administrativa',
+    resultField: 'upper_adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'docente_ens_superior',
+        field: 'par_categoria_administrativa'
+    }
+}).addValue({
+    name: 'academic_organization',
+    table: 'docente_ens_superior',
+    tableField: 'cod_organizacao_academica',
+    resultField: 'academic_organization_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        table: 'docente_ens_superior',
+        field: 'cod_organizacao_academica'
+    }
+}).addValue({
+    name:'academic_level',
+    table: 'docente_ens_superior',
+    tableField: 'cod_grau_academico',
+    resultField: 'academic_level_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_grau_academico'
+    }
+}).addValue({
+    name:'upper_education_mod',
+    table: 'docente_ens_superior',
+    tableField: 'cod_modalidade_ensino',
+    resultField: 'upper_education_mod_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_modalidade_ensino'
+    }
+}).addValue({
+    name:'teacher_situation',
+    table: 'docente_ens_superior',
+    tableField: 'par_situacao_docente',
+    resultField: 'teacher_situation_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_situacao_docente'
+    }
+}).addValue({
+    name:'work_regime',
+    table: 'docente_ens_superior',
+    tableField: 'cod_regime_trabalho',
+    resultField: 'work_regime_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_regime_trabalho'
+    }
+}).addValue({
+    name:'substitute',
+    table: 'docente_ens_superior',
+    tableField: 'docente_substituto',
+    resultField: 'substitute_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'docente_substituto'
+    }
+}).addValue({
+    name:'visitor',
+    table: 'docente_ens_superior',
+    tableField: 'docente_visitante',
+    resultField: 'visitor_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'docente_visitante'
+    }
+}).addValue({
+    name:'ead_teacher',
+    table: 'docente_ens_superior',
+    tableField: 'ministra_aula_ead',
+    resultField: 'ead_teacher_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'ministra_aula_ead'
+    }
+}).addValue({
+    name:'graduation_presential',
+    table: 'docente_ens_superior',
+    tableField: 'atua_atividade_graduacao_presencial',
+    resultField: 'graduation_presential_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'atua_atividade_graduacao_presencial'
+    }
+}).addValue({
+    name:'postgraduate_ead_teacher',
+    table: 'docente_ens_superior',
+    tableField: 'atua_atividade_posgraduacao_distancia',
+    resultField: 'postgraduate_ead_teacher_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'atua_atividade_posgraduacao_distancia'
+    }
+}).addValue({
+    name:'postgraduate_presential_teacher',
+    table: 'docente_ens_superior',
+    tableField: 'atua_atividade_posgraduacao_presencial',
+    resultField: 'postgraduate_presential_teacher_id',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'atua_atividade_posgraduacao_presencial'
+    }
+}).addValue({
+    name:'teacher_schooling',
+    table: 'docente_ens_superior',
+    tableField: 'cod_escolaridade_docente',
+    resultField: 'teacher_schooling_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_escolaridade_docente'
+    }
+}).addValue({
+    name:'ethnic_group_teacher_ies',
+    table: 'docente_ens_superior',
+    tableField: 'cod_cor_raca_docente',
+    resultField: 'ethnic_group_teacher_ies_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'cod_cor_raca_docente'
+    }
+}).addValue({
+    name:'gender_ies',
+    table: 'docente_ens_superior',
+    tableField: 'sexo_docente',
+    resultField: 'gender_ies_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'sexo_docente'
+    }
+}).addValue({
+    name:'deficiency',
+    table: 'docente_ens_superior',
+    tableField: 'par_docente_deficiencia',
+    resultField: 'deficiency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'par_docente_deficiencia'
+    }
+});
+
+teacherEnrollmentApp.get('/', rqf.parse(), (req, res, next) => {
+
+    if ("university" in req.dims) {
+        req.sql.field('COUNT(*)', 'total')
+        .field("'Brasil'", 'name')
+        .field('docente_ens_superior.ano_censo', 'year')
+        .from('docente_ens_superior')
+        .group('docente_ens_superior.cod_ies')
+        .group('docente_ens_superior.ano_censo')
+        .order('docente_ens_superior.cod_ies')
+        .order('docente_ens_superior.ano_censo')
+    }
+    else {
+        req.sql.field('COUNT(*)', 'total')
+        .field("'Brasil'", 'name')
+        .field('docente_ens_superior.ano_censo', 'year')
+        .from('docente_ens_superior')
+        .group('docente_ens_superior.ano_censo')
+        .order('docente_ens_superior.ano_censo')
+    }
+
+   next();
+},  rqf.build(), query, addMissing(rqf), id2str.transform(false), response('teacherEnrollment'));
+
+module.exports = teacherEnrollmentApp;
diff --git a/src/libs/routes_v2/user.js b/src/libs/routes_v2/user.js
new file mode 100644
index 00000000..7ff088ee
--- /dev/null
+++ b/src/libs/routes_v2/user.js
@@ -0,0 +1,305 @@
+const express = require('express');
+
+const userApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const config = require(`${libs}/config`);
+
+const log = require(`${libs}/log`)(module);
+
+const User = require(`${libs}/models/user`);
+
+const VerificationToken = require(`${libs}/models/verificationToken`);
+
+const ResetToken = require(`${libs}/models/resetToken`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const email = require(`${libs}/middlewares/email`);
+
+const passport = require('passport');
+
+function emailSyntax(email) {
+  const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
+  return regex.test(email);
+}
+
+userApp.get('/schooling', (req, res, next) => {
+  req.result = [
+    'Não estudou',
+    'Ensino Fundamental Incompleto',
+    'Ensino Fundamental Completo',
+    'Ensino Médio',
+    'Graduação',
+    'Mestrado',
+    'Doutorado'
+  ];
+  next();
+}, response('schooling'));
+
+userApp.get('/segment', (req, res, next) => {
+  req.result = [
+    'Gestores e equipe gestora das secretarias e ministério da Educação',
+    'Gestores dos órgãos de planejamento e finanças (das três esferas de governo)',
+    'Agentes do poder legislativo',
+    'Agentes dos conselhos de educação',
+    'Profissionais da educação',
+    'Sindicato',
+    'Sociedade civil interessada no financiamento da Educação Básica de qualidade',
+    'Comunidade acadêmica',
+    'Imprensa',
+    'Outro [citar segmento]'
+  ];
+  next();
+}, response('segment'));
+
+userApp.get('/role', (req, res, next) => {
+  req.result = [
+    {"Gestores e equipe gestora das secretarias e ministério da Educação" : ["Dirigente municipal, estadual e federal", "Secretário do MEC", "Servidor da área de planejamento educacional", "Membro de associação de gestores (Ex. Undime, Consed, etc)", "Outro [citar função]"]},
+    {"Gestores dos órgãos de planejamento e finanças (das três esferas de governo)" : ["Equipe gestora dos órgãos de planejamento", "Equipe gestora dos órgãos de finanças", "Outro [citar função]"]},
+    {"Agentes do poder legislativo" : ["Parlamentar", "Assessor/a parlamentar", "Auditor/a dos tribunais de conta", "Conselheiro/a de tribunais de conta.", "Outro [citar função]"]},
+    {"Agentes dos conselhos de educação" : ["Conselheiro/a municipais, estaduais e federais", "Conselheiro/a do Fundeb", "Outro [citar função]"]},
+    {"Profissionais da educação" : ["Professor/a da Educação Básica", "Profissional da educação não-docente", "Outro [citar função]"]},
+    {"Sindicato" : ["Agente de sindicatos"]},
+    {"Sociedade civil interessada no financiamento da Educação Básica de qualidade" : ["Membro de fóruns educacionais", "Membro de ONGs e demais entidades sem fins lucrativos", "Estudante da educação básica e membro de entidades estudantis", "Pais e membros de entidades de pais", "Outro [citar função]"]},
+    {"Comunidade acadêmica" : ["Pesquisador/a", "Estudantes de graduação e pós-graduação", "Representantes de entidades de pesquisa (Ex.: ANPED, ANPAE e FINEDUCA)", "Outro [citar função]"]},
+    {"Imprensa" : ["Jornalista", "Outro [citar função]"]},
+    {"Outro [citar segmento]" : []}
+  ]
+  next();
+}, response('role'));
+
+/*
+userApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, next) => {
+  User.find((err, users) => {
+    if(err) {
+      log.error(err);
+      return next(err);
+    }
+
+    let result = [];
+    users.forEach((user) => {
+      let u = user.toObject();
+      delete u.hashedPassword;
+      delete u.salt;
+      result.push(u);
+    });
+    req.result = result;
+    next();
+  });
+}, response('users'));
+*/
+
+userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+  let user = req.user.toObject();
+  delete user.hashedPassword;
+  delete user.salt;
+  req.result = user;
+  next();
+}, response('user'));
+
+userApp.get('/:id', (req, res, next) => {
+  User.findById(req.params.id, (err, user) => {
+    if(err) {
+      log.error(err);
+      return next(err);
+    }
+    if(!user) {
+      req.statusCode = 404;
+      next({msg: 'User not found'});
+    } else {
+      let u = user.toObject;
+      delete u.hashedPassword;
+      delete u.salt;
+      req.result = u;
+      next();
+    }
+  });
+}, response('user'));
+
+userApp.post('/', (req, res, next) => {
+  let user = new User({
+    email: req.body.email,
+    password: req.body.password,
+    name: req.body.name,
+    nickname: req.body.nickname,
+    cpf: req.body.cpf,
+    cep: req.body.cep,
+    complement: req.body.complement,
+    address: req.body.address,
+    phone: req.body.phone,
+    schooling: req.body.schooling,
+    course: req.body.course,
+    segment: req.body.segment,
+    role: req.body.role,
+    institutionName: req.body.institutionName,
+    state: req.body.state,
+    city: req.body.city,
+    receiveEmails: false || req.body.receiveEmails,
+    origin: req.body.origin,
+    citesegment: req.body.citesegment,
+    citerole: req.body.citerole,
+    admin: false
+  });
+
+  if (typeof req.body.password === 'undefined' || !req.body.password) {
+    res.statusCode = 400;
+    return res.json({errors: ["O campo senha é obrigatório"]});
+  } else {
+    user.save((err) => {
+      if(err) {
+        log.error(err);
+        let errors = [];
+        for(let errName in err.errors) {
+          errors.push(err.errors[errName].message);
+        }
+        log.error(errors);
+        res.statusCode = 400;
+        return res.json({err, errors});
+      }
+
+      // Create verification token
+      let verificationToken = new VerificationToken({
+        userId: user._id
+      });
+
+      verificationToken.createVerificationToken((err, token) => {
+        if(err) {
+          log.error(err);
+          return next(err);
+        }
+        let url = config.default.lde.url + '/verify';
+        let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${token}`;
+        // Send confirmation email
+        let mailOptions = {
+          to: `"${user.name} <${user.email}>"`,
+          subject: "Confirme seu cadastro - Laboratório de Dados Educacionais",
+          text
+        }
+        email(mailOptions, (err, info) => {
+          if(err) {
+            log.error(err);
+            res.json({msg: 'User created'});
+          }
+          if(info) {
+            log.info(`Message ${info.messageId} sent: ${info.response}`);
+            log.info(`Usuário ${user.email} foi criado`);
+          }
+          res.json({msg: 'User created'});
+        });
+      });
+    });
+  }
+
+});
+
+userApp.put('/:id', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+  User.findById(req.params.id, (err, user) => {
+    if (err) {
+      log.error(err);
+      return next({err});
+    }
+
+    if(!user) {
+      res.statusCode = 404;
+      return next({err: {
+        message: 'Usuário não encontrado'
+      }});
+    }
+
+    user.email = req.body.email || user.email;
+    user.name = req.body.name || user.name;
+    user.nickname = req.body.nickname || user.nickname || user.name;
+    user.cep = req.body.cep || user.cep;
+    user.complement = req.body.complement || user.complement;
+    user.address = req.body.address || user.address;
+    user.phone = req.body.phone || user.phone;
+    user.schooling = req.body.schooling || user.schooling;
+    user.course = req.body.course || user.course;
+    user.segment = req.body.segment || user.segment;
+    user.role = req.body.role || user.role;
+    user.institutionName = req.body.institutionName || user.institutionName;
+    user.state = req.body.state || user.state;
+    user.city = req.body.city || user.city;
+    user.receiveEmails = req.body.receiveEmails || user.receiveEmails;
+    user.citesegment = req.body.citesegment || user.citesegment;
+    user.citerole = req.body.citerole || user.citerole;
+
+    if ((req.body.password) && (req.body.newpassword)) {
+        if (req.body.password != req.body.newpassword) {
+            if (user.checkPassword(req.body.password)) {
+                user.password = req.body.newpassword;
+            } else {
+                res.statusCode = 500;
+                return res.json({error: {
+                    message: 'A senha atual está incorreta'
+                }});
+            }
+        } else {
+            res.statusCode = 500;
+            return res.json({error: {
+                message: 'A nova senha é a mesma da senha atual'
+            }});
+        }
+    }
+
+    user.save(err => {
+      if(err) {
+        log.error(err);
+        return next({message: 'Erro ao atualizar usuário'});
+      }
+      let u = user.toObject();
+      delete u.hashedPassword;
+      delete u.salt;
+      res.json({user: u});
+    })
+  })
+});
+
+userApp.get('/reset/password', (req, res, next) => {
+  let emailAddress = req.query.email;
+  User.findOne({email: emailAddress}, (err, user)=> {
+    if(err) {
+      log.error(err);
+      let errors = [];
+      for(let errName in err.errors) {
+        errors.push(err.errors[errName].message);
+      }
+      res.statusCode = 400;
+      return res.json({err, errors});
+    }
+    if (!user) {
+      res.statusCode = 404;
+      res.json({msg: "O usuário não está cadastrado"});
+    }
+    else {
+      let resetToken = new ResetToken({
+        userId: user._id
+      });
+      resetToken.createResetToken((err, token) => {
+        if (err) {
+          log.error(err);
+          return next(err);
+        }
+        let url = config.default.lde.url + '/reset-password';
+        let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${token}`;
+        let mailOptions = {
+          to: `"${user.name} <${user.email}>"`,
+          subject: "Redefinição de Senha - Laboratório de Dados Educacionais",
+          text
+        }
+        email(mailOptions, (err, info) => {
+          if(err) {
+            log.error(err);
+            res.json({msg: 'Undelivered Reset Password Mail'});
+          }
+          log.info(`Message ${info.messageId} sent: ${info.response}`);
+          res.json({msg: 'Reset Password Mail Successfully Delivered'});
+        });
+      })
+    }
+  })
+})
+
+module.exports = userApp;
diff --git a/src/libs/routes_v2/verifyToken.js b/src/libs/routes_v2/verifyToken.js
new file mode 100644
index 00000000..d54f64aa
--- /dev/null
+++ b/src/libs/routes_v2/verifyToken.js
@@ -0,0 +1,52 @@
+const express = require('express');
+
+const verifyTokenApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const log = require(`${libs}/log`)(module);
+
+const VerificationToken = require(`${libs}/models/verificationToken`);
+
+const User = require(`${libs}/models/user`);
+
+verifyTokenApp.get('/:token', (req, res, next) => {
+    let token = req.params.token;
+    VerificationToken.findOne({token: token}, (err, vToken) => {
+        if(err) {
+            log.error(err);
+            return next(err);
+        }
+        if(!vToken) {
+            // TODO: generate new verification token
+            res.statusCode = 404;
+            return next({msg: 'Token not found', status:404});
+        }
+        User.findById(vToken.userId, (err, user) => {
+            if(err) {
+                log.error(err);
+                next(err);
+            }
+            user.verified = true;
+            user.save((err) => {
+                if(err) {
+                    log.error(err);
+                    next(err);
+                }
+            });
+            let u = user.toObject();
+            delete u.salt;
+            delete u.hashedPassword;
+            vToken.verified = true;
+            vToken.save((err) => {
+                if(err) {
+                    log.error(err);
+                    next(err);
+                }
+            });
+            res.json({msg: 'User verified', user: u});
+        });
+    });
+});
+
+module.exports = verifyTokenApp;
-- 
GitLab


From bf9af7457d8fe8af3430ebc292067fa23d630eba Mon Sep 17 00:00:00 2001
From: ppc19 <ppc19@inf.ufpr.br>
Date: Wed, 8 Mar 2023 09:25:29 -0300
Subject: [PATCH 055/123] add adm dependency data do school dim

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

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 4f76bed3..95e1d693 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -116,8 +116,8 @@ rqf.addField({
 }, 'dims').addValueToField({
     name: 'school',
     table: 'escola',
-    tableField: ['nome_escola', 'id'],
-    resultField: ['school_name', 'school_id'],
+    tableField: ['nome_escola', 'id', 'dependencia_adm_id'], // Dado de dependencia administrativa sempre deve ser retornado com escola
+    resultField: ['school_name', 'school_id', 'adm_dependcy_id'],
     where: {
         relation: '=',
         type: 'integer',
-- 
GitLab


From 7c3dd69878604323f434bf673f0722ccc676b90b Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Wed, 8 Mar 2023 09:54:14 -0300
Subject: [PATCH 056/123] adm_dependency dim added in classroom count

---
 src/libs/routes_v2/classroomCount.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 95e1d693..59fc53c8 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -117,7 +117,7 @@ rqf.addField({
     name: 'school',
     table: 'escola',
     tableField: ['nome_escola', 'id', 'dependencia_adm_id'], // Dado de dependencia administrativa sempre deve ser retornado com escola
-    resultField: ['school_name', 'school_id', 'adm_dependcy_id'],
+    resultField: ['school_name', 'school_id', 'adm_dependency_id'],
     where: {
         relation: '=',
         type: 'integer',
@@ -432,6 +432,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             if (req.dims.school){
                 obj.school_id = integral_time.school_id
                 obj.school_name = integral_time.school_name
+                obj.adm_dependency_id = integral_time.adm_dependency_id
             }
             req.integral_time[code] = obj
             currentCodeObj = obj;
@@ -517,7 +518,8 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             };
             if (req.dims.school){
                 obj.school_id = classroom.school_id,
-                obj.school_name = classroom.school_name
+                obj.school_name = classroom.school_name,
+                obj.adm_dependency_id = classroom.adm_dependency_id
             }
             if (req.teacherCalc)
                 obj.percentage_teacher_career = [];
-- 
GitLab


From 2bebadbe91fa97e6440567551ec2eff732c09686 Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Wed, 8 Mar 2023 10:06:58 -0300
Subject: [PATCH 057/123] adm_dependency_name added in classroom count route

---
 src/libs/routes_v2/classroomCount.js | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 59fc53c8..0af32828 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -433,6 +433,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 obj.school_id = integral_time.school_id
                 obj.school_name = integral_time.school_name
                 obj.adm_dependency_id = integral_time.adm_dependency_id
+                obj.adm_dependency_name = integral_time.adm_dependency_name
             }
             req.integral_time[code] = obj
             currentCodeObj = obj;
@@ -519,7 +520,8 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             if (req.dims.school){
                 obj.school_id = classroom.school_id,
                 obj.school_name = classroom.school_name,
-                obj.adm_dependency_id = classroom.adm_dependency_id
+                obj.adm_dependency_id = classroom.adm_dependency_id,
+                obj.adm_dependency_name = classroom.adm_dependency_name
             }
             if (req.teacherCalc)
                 obj.percentage_teacher_career = [];
-- 
GitLab


From 628bb714c7f6a559fe67d08236a8b4f609f57be6 Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Mon, 6 Mar 2023 11:26:46 -0300
Subject: [PATCH 058/123] filter added

---
 src/libs/routes_v1/school.js | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/src/libs/routes_v1/school.js b/src/libs/routes_v1/school.js
index 73b765e2..cbb1b289 100644
--- a/src/libs/routes_v1/school.js
+++ b/src/libs/routes_v1/school.js
@@ -308,6 +308,16 @@ rqf.addField({
         type: 'integer',
         field: 'localizacao_diferenciada_par'
     }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'escola',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
 });
 
 rqfCount.addField({
-- 
GitLab


From bcec2ad0517ffa21b94bbd55b04d842df801ab3c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 6 Mar 2023 10:26:06 -0300
Subject: [PATCH 059/123] Add reqBody middleware processing to school route in
 v2

---
 src/libs/routes_v2/school.js | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/libs/routes_v2/school.js b/src/libs/routes_v2/school.js
index 172361c1..2c60d9c0 100644
--- a/src/libs/routes_v2/school.js
+++ b/src/libs/routes_v2/school.js
@@ -16,6 +16,8 @@ const id2str = require(`${libs}/middlewares/id2str`);
 
 const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
 
+const ReqBody = require(`${libs}/middlewares/reqBody`);
+
 const request = require(`request`);
 
 const config = require(`${libs}/config`);
@@ -28,6 +30,7 @@ const cache = require('apicache').options({ debug: config.debug, statusCodes: {i
 
 let rqf = new ReqQueryFields();
 let rqfCount = new ReqQueryFields();
+let reqBody = new ReqBody();
 
 // Return location
 schoolApp.get('/year_range', cache('15 day'), (req, res, next) => {
@@ -655,11 +658,14 @@ schoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     next();
 }, query, response('school'));
 
-schoolApp.get('/count', cache('15 day'), rqfCount.parse(), (req, res, next) => {
+schoolApp.get('/count', cache('15 day'), rqfCount.parse(), reqBody.parse(), (req, res, next) => {
 	let arrang = ["arranjo_creche", "arranjo_pre", "arranjo_fundamental_ai", "arranjo_fundamental_af", "arranjo_multietapa", "arranjo_ensino_medio", "ensino_eja", "educacao_profissional", "ensino_especial"];
 
+    if (!req.hasMetrics) {
+        req.sql.field('COUNT(escola.id)', 'total');
+    }
+
     req.sql.from('escola')
-        .field('COUNT(escola.id)', 'total')
         .field("'Brasil'", 'name')
         .field('escola.ano_censo', 'year')
         .group('escola.ano_censo')
-- 
GitLab


From 9bdcd5ebe534c4264fc0574af1e64d7f4e318584 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 7 Mar 2023 10:21:30 -0300
Subject: [PATCH 060/123] Add v2 school infrastructure route

---
 src/libs/routes_v1/school.js               |  30 ++++
 src/libs/routes_v2/school.js               | 157 ++++++++++--------
 src/libs/routes_v2/schoolInfrastructure.js | 180 ++++++++++-----------
 3 files changed, 212 insertions(+), 155 deletions(-)

diff --git a/src/libs/routes_v1/school.js b/src/libs/routes_v1/school.js
index cbb1b289..768325c3 100644
--- a/src/libs/routes_v1/school.js
+++ b/src/libs/routes_v1/school.js
@@ -261,6 +261,36 @@ rqf.addField({
         field: 'ano_censo',
         table: 'escola'
     }
+}).addValue({
+    name: 'min_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'escola',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'escola',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
 }).addField({
     name: 'search',
     field: true,
diff --git a/src/libs/routes_v2/school.js b/src/libs/routes_v2/school.js
index 2c60d9c0..54126d80 100644
--- a/src/libs/routes_v2/school.js
+++ b/src/libs/routes_v2/school.js
@@ -34,22 +34,22 @@ let reqBody = new ReqBody();
 
 // Return location
 schoolApp.get('/year_range', cache('15 day'), (req, res, next) => {
-    req.sql.from('escola')
-    .field('MIN(escola.ano_censo)', 'start_year')
-    .field('MAX(escola.ano_censo)', 'end_year');
+    req.sql.from('escola_agregada')
+    .field('MIN(escola_agregada.ano_censo)', 'start_year')
+    .field('MAX(escola_agregada.ano_censo)', 'end_year');
     next();
 }, query, response('range'));
 
 schoolApp.get('/years', cache('15 day'), (req, res, next) => {
-    req.sql.from('escola').
-    field('DISTINCT escola.ano_censo', 'year');
+    req.sql.from('escola_agregada').
+    field('DISTINCT escola_agregada.ano_censo', 'year');
     next();
 }, query, response('years'));
 
 schoolApp.get('/source', (req, res, next) => {
     req.sql.from('fonte')
     .field('fonte', 'source')
-    .where('tabela = \'escola\'');
+    .where('tabela = \'escola_agregada\'');
     next();
 }, query, response('source'));
 
@@ -214,7 +214,7 @@ rqf.addField({
     where: true
 }).addValue({
     name: 'id',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'id',
     where: {
         relation: '=',
@@ -230,12 +230,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'state',
@@ -246,23 +246,53 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'estado_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'estado_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
         relation: '=',
         type: 'integer',
         field: 'ano_censo',
-        table: 'escola'
+        table: 'escola_agregada'
+    }
+}).addValue({
+    name: 'min_year',
+    table: 'escola_agregada',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '>=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'max_year',
+    table: 'escola_agregada',
+    tableField: 'ano_censo',
+    resultField: 'year',
+    where: {
+        relation: '<=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'escola_agregada',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
     }
 }).addValue({
     name: 'min_year',
@@ -302,7 +332,7 @@ rqf.addField({
     join: {
       primary: 'id',
       foreign: 'municipio_id',
-      foreignTable: 'escola'
+      foreignTable: 'escola_agregada'
     }
 }, 'search')
 .addValueToField({
@@ -319,11 +349,11 @@ rqf.addField({
     join: {
       primary: 'id',
       foreign: 'estado_id',
-      foreignTable: 'escola'
+      foreignTable: 'escola_agregada'
     }
 }, 'search').addValue({
     name: 'diff_location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localizacao_diferenciada_par',
     resultField: 'diff_location_id',
     where: {
@@ -343,7 +373,7 @@ rqfCount.addField({
     where: false
 }).addValue({
     name: 'id',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'id',
     where: {
         relation: '=',
@@ -359,12 +389,12 @@ rqfCount.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'dims').addValueToField({
     name: 'city',
@@ -375,12 +405,12 @@ rqfCount.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'filter').addValue({
     name: 'state',
@@ -391,12 +421,12 @@ rqfCount.addField({
         relation: '=',
         type: 'integer',
         field: 'estado_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'estado_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'mesoregion',
@@ -412,7 +442,7 @@ rqfCount.addField({
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'microregion',
@@ -428,7 +458,7 @@ rqfCount.addField({
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'region',
@@ -443,22 +473,22 @@ rqfCount.addField({
     join: {
         primary: 'id',
         foreign: 'regiao_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
         relation: '=',
         type: 'integer',
         field: 'ano_censo',
-        table: 'escola'
+        table: 'escola_agregada'
     }
 }).addValue({
     name: 'location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localizacao_id',
     resultField: 'location_id',
     where: {
@@ -468,7 +498,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'diff_location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localizacao_diferenciada_par',
     resultField: 'diff_location_id',
     where: {
@@ -478,7 +508,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'rural_location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localidade_area_rural',
     resultField: 'rural_location_id',
     where: {
@@ -488,7 +518,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'arrangement',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'arranjo',
     resultField: 'arrangement_id',
     where: {
@@ -498,7 +528,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'adm_dependency',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_id',
     resultField: 'adm_dependency_id',
     where: {
@@ -508,7 +538,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'adm_dependency_detailed',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_priv',
     resultField: 'adm_dependency_detailed_id',
     where: {
@@ -518,7 +548,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'government_agreement',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_convenio_publico',
     resultField: 'government_agreement_id',
     where: {
@@ -528,7 +558,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'integral_time',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'tempo_integral',
     resultField: 'integral_time_id',
     where: {
@@ -538,7 +568,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'agreement',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'tipo_convenio_pp',
     resultField: 'agreement_id',
     where: {
@@ -548,7 +578,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'education_day_care_child',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'reg_infantil_creche_t1',
     resultField: 'education_day_care_child_id',
     where: {
@@ -558,7 +588,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'education_preschool_child',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'reg_infantil_preescola_t1',
     resultField: 'education_preschool_child_id',
     where: {
@@ -568,7 +598,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'education_begin_elementary_school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'reg_fund_ai_t1',
     resultField: 'education_begin_elementary_school_id',
     where: {
@@ -578,7 +608,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'education_end_elementary_school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'reg_fund_af_t1',
     resultField: 'education_end_elementary_school_id',
     where: {
@@ -588,7 +618,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'education_middle_school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'reg_medio_medio_t1',
     resultField: 'education_middle_school_id',
     where: {
@@ -598,7 +628,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'education_professional',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'educacao_profissional',
     resultField: 'education_professional_id',
     where: {
@@ -608,7 +638,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'education_eja',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ensino_eja',
     resultField: 'education_eja_id',
     where: {
@@ -618,7 +648,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'min_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -628,7 +658,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'max_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -638,7 +668,7 @@ rqfCount.addField({
     }
 }).addValue({
     name: 'school_building',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'local_func_predio_escolar',
     resultField: 'school_building',
     where: {
@@ -649,37 +679,34 @@ rqfCount.addField({
 });
 schoolApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
 
-    req.sql.from('escola')
-        .field('escola.id')
-        .field('escola.ano_censo', 'year')
-        .field('escola.nome_escola', 'name')
-        .field('escola.estado_id', 'state_id')
-        .field('escola.municipio_id', 'city_id');
+    req.sql.from('escola_agregada')
+        .field('escola_agregada.id')
+        .field('escola_agregada.ano_censo', 'year')
+        .field('escola_agregada.nome_escola', 'name')
+        .field('escola_agregada.estado_id', 'state_id')
+        .field('escola_agregada.municipio_id', 'city_id');
     next();
 }, query, response('school'));
 
-schoolApp.get('/count', cache('15 day'), rqfCount.parse(), reqBody.parse(), (req, res, next) => {
+schoolApp.get('/count', cache('15 day'), rqfCount.parse(), (req, res, next) => {
 	let arrang = ["arranjo_creche", "arranjo_pre", "arranjo_fundamental_ai", "arranjo_fundamental_af", "arranjo_multietapa", "arranjo_ensino_medio", "ensino_eja", "educacao_profissional", "ensino_especial"];
 
-    if (!req.hasMetrics) {
-        req.sql.field('COUNT(escola.id)', 'total');
-    }
-
-    req.sql.from('escola')
+    req.sql.from('escola_agregada')
+        .field('COUNT(escola_agregada.id)', 'total')
         .field("'Brasil'", 'name')
-        .field('escola.ano_censo', 'year')
-        .group('escola.ano_censo')
-        .order('escola.ano_censo')
-        .where('escola.situacao_funcionamento_pareada = 1 AND (escola.ensino_regular = 1 OR escola.ensino_eja=1 or escola.educacao_profissional=1)')
+        .field('escola_agregada.ano_censo', 'year')
+        .group('escola_agregada.ano_censo')
+        .order('escola_agregada.ano_censo')
+        .where('escola_agregada.situacao_funcionamento_pareada = 1 AND (escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja=1 or escola_agregada.educacao_profissional=1)')
 
 	//Transforma a query em OR se tiver o filtro do arranjo
 	if (req.filter.arrangement) {
 		let arrangementQuery = "";
 		for (let i = 0; i < req.filter.arrangement.length - 1; i++) {
-			arrangementQuery += 'escola.' + arrang[req.filter.arrangement[i]] + ' = 1 OR ';
+			arrangementQuery += 'escola_agregada.' + arrang[req.filter.arrangement[i]] + ' = 1 OR ';
 		}
 		// o ultimo elemento precisa ser sem o OR
-		arrangementQuery += 'escola.' + arrang[req.filter.arrangement[req.filter.arrangement.length - 1]] + ' = 1';
+		arrangementQuery += 'escola_agregada.' + arrang[req.filter.arrangement[req.filter.arrangement.length - 1]] + ' = 1';
 		req.sql.where('' + arrangementQuery);
 	}
 	delete req.filter.arrangement
diff --git a/src/libs/routes_v2/schoolInfrastructure.js b/src/libs/routes_v2/schoolInfrastructure.js
index c74f0e30..da4d4593 100644
--- a/src/libs/routes_v2/schoolInfrastructure.js
+++ b/src/libs/routes_v2/schoolInfrastructure.js
@@ -47,22 +47,22 @@ let rqf = new ReqQueryFields();
 infrastructureApp.use(cache('15 day'));
 
 infrastructureApp.get('/year_range', (req, res, next) => {
-    req.sql.from('escola')
-    .field('MIN(escola.ano_censo)', 'start_year')
-    .field('MAX(escola.ano_censo)', 'end_year');
+    req.sql.from('escola_agregada')
+    .field('MIN(escola_agregada.ano_censo)', 'start_year')
+    .field('MAX(escola_agregada.ano_censo)', 'end_year');
     next();
 }, query, response('range'));
 
 infrastructureApp.get('/years', (req, res, next) => {
-    req.sql.from('escola')
-    .field('DISTINCT escola.ano_censo', 'year');
+    req.sql.from('escola_agregada')
+    .field('DISTINCT escola_agregada.ano_censo', 'year');
     next();
 }, query, response('years'));
 
 infrastructureApp.get('/source', (req, res, next) => {
     req.sql.from('fonte')
     .field('fonte', 'source')
-    .where('tabela = \'escola\'');
+    .where('tabela = \'escola_agregada\'');
     next();
 }, query, response('source'));
 
@@ -125,12 +125,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'dims').addValueToField({
     name: 'city',
@@ -141,12 +141,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'filter').addValueToField({
     name: 'state',
@@ -157,12 +157,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'estado_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'estado_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'dims').addValueToField({
     name: 'state',
@@ -173,12 +173,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'estado_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'estado_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'filter').addValue({
     name: 'region',
@@ -193,11 +193,11 @@ rqf.addField({
     join: {
         primary: 'id',
         foreign: 'regiao_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localizacao_id',
     resultField: 'location_id',
     where: {
@@ -207,7 +207,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'rural_location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localidade_area_rural',
     resultField: 'rural_location_id',
     where: {
@@ -217,7 +217,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'adm_dependency',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_id',
     resultField: 'adm_dependency_id',
     where: {
@@ -227,7 +227,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'adm_dependency_detailed',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_priv',
     resultField: 'adm_dependency_detailed_id',
     where: {
@@ -237,7 +237,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'min_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -247,7 +247,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'max_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -328,57 +328,57 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
 
     // Local de funcionamento
     let allSchools = req.sql.clone();
-    allSchools.from('escola').field('COUNT(escola.id)', 'total')
+    allSchools.from('escola_agregada').field('COUNT(escola_agregada.id)', 'total')
     .field("'Brasil'", 'name')
-    .field('escola.ano_censo', 'year')
-    .group('escola.ano_censo')
-    .where('escola.situacao_de_funcionamento = 1')
-    .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1')
-    .where('escola.local_func_predio_escolar = 1')
-    .where('escola.dependencia_adm_id <= 3')
-    .order('escola.ano_censo');
+    .field('escola_agregada.ano_censo', 'year')
+    .group('escola_agregada.ano_censo')
+    .where('escola_agregada.situacao_de_funcionamento = 1')
+    .where('escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1')
+    .where('escola_agregada.local_func_predio_escolar = 1')
+    .where('escola_agregada.dependencia_adm_id <= 3')
+    .order('escola_agregada.ano_censo');
     req.queryIndex.allSchools = req.querySet.push(allSchools) - 1;
 
     let allUrbanSchools = allSchools.clone();
-    allUrbanSchools.where('escola.localizacao_id = 1');
+    allUrbanSchools.where('escola_agregada.localizacao_id = 1');
     req.queryIndex.allUrbanSchools = req.querySet.push(allUrbanSchools) - 1;
 
     let allCountrySchools = allSchools.clone();
-    allCountrySchools.where('escola.localizacao_id = 2');
+    allCountrySchools.where('escola_agregada.localizacao_id = 2');
     req.queryIndex.allCountrySchools = req.querySet.push(allCountrySchools) - 1;
 
     let allSchoolsNotSchoolBuilding = req.sql.clone();
-    allSchoolsNotSchoolBuilding.from('escola').field('COUNT(escola.id)', 'total')
+    allSchoolsNotSchoolBuilding.from('escola_agregada').field('COUNT(escola_agregada.id)', 'total')
     .field("'Brasil'", 'name')
-    .field('escola.ano_censo', 'year')
-    .group('escola.ano_censo')
-    .where('escola.situacao_de_funcionamento = 1')
-    .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1')
-    .where('escola.local_func_predio_escolar = 0')
-    .where('escola.dependencia_adm_id <= 3')
-    .order('escola.ano_censo');
+    .field('escola_agregada.ano_censo', 'year')
+    .group('escola_agregada.ano_censo')
+    .where('escola_agregada.situacao_de_funcionamento = 1')
+    .where('escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1')
+    .where('escola_agregada.local_func_predio_escolar = 0')
+    .where('escola_agregada.dependencia_adm_id <= 3')
+    .order('escola_agregada.ano_censo');
     req.queryIndex.allSchoolsNotSchoolBuilding = req.querySet.push(allSchoolsNotSchoolBuilding) - 1;
 
     // Bibliotecas
     req.queryIndex.allLibraries = req.queryIndex.allUrbanSchools;
 
     let haveLibraries = allUrbanSchools.clone();
-    haveLibraries.where('escola.biblioteca = 1');
+    haveLibraries.where('escola_agregada.biblioteca = 1');
     req.queryIndex.haveLibraries = req.querySet.push(haveLibraries) - 1;
 
     let needLibraries = allUrbanSchools.clone();
-    needLibraries.where('escola.biblioteca = 0');
+    needLibraries.where('escola_agregada.biblioteca = 0');
     req.queryIndex.needLibraries = req.querySet.push(needLibraries) - 1;
 
     // Sala de leitura
     req.queryIndex.allLibrariesReadingRoom = req.queryIndex.allCountrySchools;
 
     let haveLibrariesReadingRoom = allCountrySchools.clone();
-    haveLibrariesReadingRoom.where('escola.biblioteca_sala_leitura = true');
+    haveLibrariesReadingRoom.where('escola_agregada.biblioteca_sala_leitura = true');
     req.queryIndex.haveLibrariesReadingRoom = req.querySet.push(haveLibrariesReadingRoom) - 1;
 
     let needLibrariesReadingRoom = allCountrySchools.clone();
-    needLibrariesReadingRoom.where('escola.biblioteca_sala_leitura = false');
+    needLibrariesReadingRoom.where('escola_agregada.biblioteca_sala_leitura = false');
     req.queryIndex.needLibrariesReadingRoom = req.querySet.push(needLibrariesReadingRoom) - 1;
 
     // Laboratório de informática
@@ -391,11 +391,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1;
 
     let haveInfLab = allInfLab.clone();
-    haveInfLab.where('escola.lab_informatica = 1');
+    haveInfLab.where('escola_agregada.lab_informatica = 1');
     req.queryIndex.haveInfLab = req.querySet.push(haveInfLab) - 1;
 
     let needInfLab = allInfLab.clone();
-    needInfLab.where('escola.lab_informatica = 0');
+    needInfLab.where('escola_agregada.lab_informatica = 0');
     req.queryIndex.needInfLab = req.querySet.push(needInfLab) - 1;
 
     // Laboratório de ciências
@@ -404,11 +404,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1;
 
     let haveScienceLab = allScienceLab.clone();
-    haveScienceLab.where('escola.lab_ciencias = true');
+    haveScienceLab.where('escola_agregada.lab_ciencias = true');
     req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1;
 
     let needScienceLab = allScienceLab.clone();
-    needScienceLab.where('escola.lab_ciencias = false');
+    needScienceLab.where('escola_agregada.lab_ciencias = false');
     req.queryIndex.needScienceLab = req.querySet.push(needScienceLab) - 1;
 
     // Parque infantil
@@ -419,24 +419,24 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1;
 
     let haveKidsPark = allKidsPark.clone();
-    haveKidsPark.where('escola.parque_infantil = 1');
+    haveKidsPark.where('escola_agregada.parque_infantil = 1');
     req.queryIndex.haveKidsPark = req.querySet.push(haveKidsPark) - 1;
 
     let needKidsPark = allKidsPark.clone();
-    needKidsPark.where('escola.parque_infantil = 0');
+    needKidsPark.where('escola_agregada.parque_infantil = 0');
     req.queryIndex.needKidsPark = req.querySet.push(needKidsPark) - 1;
 
     // // Berçário
     // let allCribs = allSchools.clone();
-    // allCribs.where('escola.reg_infantil_creche_t1 = 1');
+    // allCribs.where('escola_agregada.reg_infantil_creche_t1 = 1');
     // req.queryIndex.allCribs = req.querySet.push(allCribs) - 1;
 
     // let haveCribs = allCribs.clone();
-    // haveCribs.where('escola.bercario = 1');
+    // haveCribs.where('escola_agregada.bercario = 1');
     // req.queryIndex.haveCribs = req.querySet.push(haveCribs) - 1;
 
     // let needCribs = allCribs.clone();
-    // needCribs.where('escola.bercario = 0');
+    // needCribs.where('escola_agregada.bercario = 0');
     // req.queryIndex.needCribs = req.querySet.push(needCribs) - 1;
 
     // Quadra de esportes
@@ -445,11 +445,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1;
 
     let haveSportsCourt = allSportsCourt.clone();
-    haveSportsCourt.where('escola.quadra_esportes = 1');
+    haveSportsCourt.where('escola_agregada.quadra_esportes = 1');
     req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1;
 
     let needSportsCourt = allSportsCourt.clone();
-    needSportsCourt.where('escola.quadra_esportes = 0');
+    needSportsCourt.where('escola_agregada.quadra_esportes = 0');
     req.queryIndex.needSportsCourt = req.querySet.push(needSportsCourt) - 1;
 
     // Quadras a serem cobertas
@@ -457,7 +457,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     // (dependencia_adm_id<=3) and (reg_fund_ai_t1=1  or reg_fund_af_t1=1 or  reg_medio_medio_t1=1  or  ensino_eja_fund= 1 or ensino_eja_medio= 1 or 
     // ensino_eja_prof= 1 or esp_eja_fund=1 or esp_eja_medio=1 or ensino_esp_exclusiva_eja_prof=1) and (quadra_esportes_descoberta=1) então conta id
     let allSportsCourtCoverage = allSportsCourt.clone();
-    allSportsCourtCoverage.where('escola.quadra_esportes_descoberta = 1');
+    allSportsCourtCoverage.where('escola_agregada.quadra_esportes_descoberta = 1');
     req.queryIndex.allSportsCourtCoverage = req.querySet.push(allSportsCourtCoverage) -1;
 
     req.queryIndex.haveSportsCourtCoverage = req.queryIndex.allSportsCourtCoverage; // It must be []
@@ -468,16 +468,16 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allCourtyard = req.queryIndex.allSchools;
 
     let haveCourtyard = allSchools.clone();
-    haveCourtyard.where('escola.patio = 1 OR escola.patio = 2');
+    haveCourtyard.where('escola_agregada.patio = 1 OR escola_agregada.patio = 2');
     req.queryIndex.haveCourtyard = req.querySet.push(haveCourtyard) - 1;
 
     let needCourtyard = allSchools.clone();
-    needCourtyard.where('escola.patio = 0');
+    needCourtyard.where('escola_agregada.patio = 0');
     req.queryIndex.needCourtyard = req.querySet.push(needCourtyard) - 1;
 
     // Pátios a serem cobertos
     let allCourtyardCoverage = allSchools.clone();
-    allCourtyardCoverage.where('escola.patio = 1');
+    allCourtyardCoverage.where('escola_agregada.patio = 1');
     req.queryIndex.allCourtyardCoverage = req.querySet.push(allCourtyardCoverage) - 1;
 
     req.queryIndex.haveCourtyardCoverage = req.queryIndex.allCourtyardCoverage; // It must be []
@@ -488,77 +488,77 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allDirectorRoom = req.queryIndex.allUrbanSchools;
 
     let haveDirectorRoom = allUrbanSchools.clone();
-    haveDirectorRoom.where('escola.sala_diretoria = 1');
+    haveDirectorRoom.where('escola_agregada.sala_diretoria = 1');
     req.queryIndex.haveDirectorRoom = req.querySet.push(haveDirectorRoom) - 1;
 
     let needDirectorRoom = allUrbanSchools.clone();
-    needDirectorRoom.where('escola.sala_diretoria = 0');
+    needDirectorRoom.where('escola_agregada.sala_diretoria = 0');
     req.queryIndex.needDirectorRoom = req.querySet.push(needDirectorRoom) - 1;
 
     // Secretaria
     req.queryIndex.allSecretary = req.queryIndex.allSchools;
 
     let haveSecretary = allSchools.clone();
-    haveSecretary.where('escola.secretaria = 1');
+    haveSecretary.where('escola_agregada.secretaria = 1');
     req.queryIndex.haveSecretary = req.querySet.push(haveSecretary) - 1;
 
     let needSecretary = allSchools.clone();
-    needSecretary.where('escola.secretaria = 0');
+    needSecretary.where('escola_agregada.secretaria = 0');
     req.queryIndex.needSecretary = req.querySet.push(needSecretary) - 1;
 
     // Sala de professores
     req.queryIndex.allTeacherRoom = req.queryIndex.allSchools;
 
     let haveTeacherRoom = allSchools.clone();
-    haveTeacherRoom.where('escola.sala_professor = 1');
+    haveTeacherRoom.where('escola_agregada.sala_professor = 1');
     req.queryIndex.haveTeacherRoom = req.querySet.push(haveTeacherRoom) - 1;
 
     let needTeacherRoom = allSchools.clone();
-    needTeacherRoom.where('escola.sala_professor = 0');
+    needTeacherRoom.where('escola_agregada.sala_professor = 0');
     req.queryIndex.needTeacherRoom = req.querySet.push(needTeacherRoom) - 1;
 
     // Cozinha
     req.queryIndex.allKitchen = req.queryIndex.allSchools;
 
     let haveKitchen = allSchools.clone();
-    haveKitchen.where('escola.cozinha = 1');
+    haveKitchen.where('escola_agregada.cozinha = 1');
     req.queryIndex.haveKitchen = req.querySet.push(haveKitchen) - 1;
 
     let needKitchen = allSchools.clone();
-    needKitchen.where('escola.cozinha = 0');
+    needKitchen.where('escola_agregada.cozinha = 0');
     req.queryIndex.needKitchen = req.querySet.push(needKitchen) - 1;
 
     // Despensa
     req.queryIndex.allStoreroom = req.queryIndex.allSchools;
 
     let haveStoreroom = allSchools.clone();
-    haveStoreroom.where('escola.despensa = 1');
+    haveStoreroom.where('escola_agregada.despensa = 1');
     req.queryIndex.haveStoreroom = req.querySet.push(haveStoreroom) - 1;
 
     let needStoreroom = allSchools.clone();
-    needStoreroom.where('escola.despensa = 0');
+    needStoreroom.where('escola_agregada.despensa = 0');
     req.queryIndex.needStoreroom = req.querySet.push(needStoreroom) - 1;
 
     // Almoxarifado
     req.queryIndex.allWarehouse = req.queryIndex.allSchools;
 
     let haveWarehouse = allSchools.clone();
-    haveWarehouse.where('escola.almoxarifado = 1');
+    haveWarehouse.where('escola_agregada.almoxarifado = 1');
     req.queryIndex.haveWarehouse = req.querySet.push(haveWarehouse) - 1;
 
     let needWarehouse = allSchools.clone();
-    needWarehouse.where('escola.almoxarifado = 0');
+    needWarehouse.where('escola_agregada.almoxarifado = 0');
     req.queryIndex.needWarehouse = req.querySet.push(needWarehouse) - 1;
 
     // Internet
     req.queryIndex.allInternet = req.queryIndex.allCountrySchools;
 
     let haveInternet = allCountrySchools.clone();
-    haveInternet.where('escola.internet = 1');
+    haveInternet.where('escola_agregada.internet = 1');
     req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1;
 
     let needInternet = allCountrySchools.clone();
-    needInternet.where('escola.internet = 0');
+    needInternet.where('escola_agregada.internet = 0');
     req.queryIndex.needInternet = req.querySet.push(needInternet) - 1;
 
     // Internet banda larga
@@ -567,22 +567,22 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allBroadbandInternet = req.queryIndex.allUrbanSchools;
 
     let haveBroadbandInternet = allUrbanSchools.clone();
-    haveBroadbandInternet.where('escola.internet_banda_larga = 1');
+    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1');
     req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1;
 
     let needBroadbandInternet = allUrbanSchools.clone();
-    needBroadbandInternet.where('escola.internet_banda_larga = 0');
+    needBroadbandInternet.where('escola_agregada.internet_banda_larga = 0');
     req.queryIndex.needBroadbandInternet = req.querySet.push(needBroadbandInternet) - 1;
 
     // Banheiro
     req.queryIndex.allInsideBathroom = req.queryIndex.allSchools;
 
     let haveInsideBathroom = allSchools.clone();
-    haveInsideBathroom.where('escola.banheiro = 1');
+    haveInsideBathroom.where('escola_agregada.banheiro = 1');
     req.queryIndex.haveInsideBathroom = req.querySet.push(haveInsideBathroom) - 1;
 
     let needInsideBathroom = allSchools.clone();
-    needInsideBathroom.where('escola.banheiro = 0');
+    needInsideBathroom.where('escola_agregada.banheiro = 0');
     req.queryIndex.needInsideBathroom = req.querySet.push(needInsideBathroom) - 1;
 
     // Banheiro adequado para educação infantil dentro do prédio
@@ -593,77 +593,77 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInsideKidsBathroom = req.querySet.push(allInsideKidsBathroom) - 1;
 
     let haveInsideKidsBathroom = allInsideKidsBathroom.clone();
-    haveInsideKidsBathroom.where('escola.sanitario_ei = 1');
+    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1');
     req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1;
 
     let needInsideKidsBathroom = allInsideKidsBathroom.clone();
-    needInsideKidsBathroom.where('escola.sanitario_ei = 0');
+    needInsideKidsBathroom.where('escola_agregada.sanitario_ei = 0');
     req.queryIndex.needInsideKidsBathroom = req.querySet.push(needInsideKidsBathroom) - 1;
 
     // Fornecimento de energia
     req.queryIndex.allEletricPower = req.queryIndex.allSchools;
 
     let haveEletricPower = allSchools.clone();
-    haveEletricPower.where('escola.energia_inexistente = 0');
+    haveEletricPower.where('escola_agregada.energia_inexistente = 0');
     req.queryIndex.haveEletricPower = req.querySet.push(haveEletricPower) - 1;
 
     let needEletricPower = allSchools.clone();
-    needEletricPower.where('escola.energia_inexistente = 1');
+    needEletricPower.where('escola_agregada.energia_inexistente = 1');
     req.queryIndex.needEletricPower = req.querySet.push(needEletricPower) - 1;
 
     // Abastecimento de água
     req.queryIndex.allWaterSupply = req.queryIndex.allSchools;
 
     let haveWaterSupply = allSchools.clone();
-    haveWaterSupply.where('escola.agua_inexistente = 0');
+    haveWaterSupply.where('escola_agregada.agua_inexistente = 0');
     req.queryIndex.haveWaterSupply = req.querySet.push(haveWaterSupply) - 1;
 
     let needWaterSupply = allSchools.clone();
-    needWaterSupply.where('escola.agua_inexistente = 1');
+    needWaterSupply.where('escola_agregada.agua_inexistente = 1');
     req.queryIndex.needWaterSupply = req.querySet.push(needWaterSupply) - 1;
 
     // Água Potável
     req.queryIndex.allFilteredWater = req.queryIndex.allSchools;
 
     let haveFilteredWater = allSchools.clone();
-    haveFilteredWater.where('escola.agua_potavel = 1');
+    haveFilteredWater.where('escola_agregada.agua_potavel = 1');
     req.queryIndex.haveFilteredWater = req.querySet.push(haveFilteredWater) - 1;
 
     let needFilteredWater = allSchools.clone();
-    needFilteredWater.where('escola.agua_potavel = 0');
+    needFilteredWater.where('escola_agregada.agua_potavel = 0');
     req.queryIndex.needFilteredWater = req.querySet.push(needFilteredWater) - 1;
 
     // Coleta de esgoto
     req.queryIndex.allSewage = req.queryIndex.allSchools;
 
     let haveSewage = allSchools.clone();
-    haveSewage.where('escola.esgoto_rede_publica = 1 OR escola.esgoto_fossa_septica = 1');
+    haveSewage.where('escola_agregada.esgoto_rede_publica = 1 OR escola_agregada.esgoto_fossa_septica = 1');
     req.queryIndex.haveSewage = req.querySet.push(haveSewage) - 1;
 
     let needSewage = allSchools.clone();
-    needSewage.where('escola.esgoto_rede_publica = 0 AND escola.esgoto_fossa_septica = 0');
+    needSewage.where('escola_agregada.esgoto_rede_publica = 0 AND escola_agregada.esgoto_fossa_septica = 0');
     req.queryIndex.needSewage = req.querySet.push(needSewage) - 1;
 
     // Dependências adaptada para pessoas com deficiências
     req.queryIndex.allAdaptedBuilding = req.queryIndex.allSchools;
 
     let haveAdaptedBuilding = allSchools.clone();
-    haveAdaptedBuilding.where('escola.acessibilidade_inexistente = 0');
+    haveAdaptedBuilding.where('escola_agregada.acessibilidade_inexistente = 0');
     req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1;
 
     let needAdaptedBuilding = allSchools.clone();
-    needAdaptedBuilding.where('escola.acessibilidade_inexistente = 1');
+    needAdaptedBuilding.where('escola_agregada.acessibilidade_inexistente = 1');
     req.queryIndex.needAdaptedBuilding = req.querySet.push(needAdaptedBuilding) - 1;
 
     // Banheiros adaptados para pessoas com deficiências
     req.queryIndex.allSpecialBathroom = req.queryIndex.allSchools;
 
     let haveSpecialBathroom = allSchools.clone();
-    haveSpecialBathroom.where('escola.sanitario_pne = 1');
+    haveSpecialBathroom.where('escola_agregada.sanitario_pne = 1');
     req.queryIndex.haveSpecialBathroom = req.querySet.push(haveSpecialBathroom) - 1;
 
     let needSpecialBathroom = allSchools.clone();
-    needSpecialBathroom.where('escola.sanitario_pne = 0');
+    needSpecialBathroom.where('escola_agregada.sanitario_pne = 0');
     req.queryIndex.needSpecialBathroom = req.querySet.push(needSpecialBathroom) - 1;
 
 
-- 
GitLab


From e91d3829c89124908ba50bf41cc4a70c47441152 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 7 Mar 2023 10:42:43 -0300
Subject: [PATCH 061/123] Change classCount to use new table

---
 src/libs/routes_v2/classCount.js        | 14 +++++++-------
 src/libs/routes_v2/dailyChargeAmount.js |  2 +-
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/libs/routes_v2/classCount.js b/src/libs/routes_v2/classCount.js
index ffdbe34f..77716328 100644
--- a/src/libs/routes_v2/classCount.js
+++ b/src/libs/routes_v2/classCount.js
@@ -196,7 +196,7 @@ rqf.addField({
   }
 }).addValueToField({
   name: 'school',
-  table: 'escola',
+  table: 'escola_agregada',
   tableField: ['nome_escola', 'id'],
   resultField: ['school_name', 'school_id'],
   where: {
@@ -211,7 +211,7 @@ rqf.addField({
   }
 }, 'dims').addValueToField({
   name: 'school',
-  table: 'escola',
+  table: 'escola_agregada',
   tableField: 'nome_escola',
   resultField: 'school_name',
   where: {
@@ -227,15 +227,15 @@ rqf.addField({
 }, 'filter');
 
 classCountApp.get('/year_range', (req, res, next) => {
-  req.sql.from('escola')
-  .field('MIN(escola.ano_censo)', 'start_year')
-  .field('MAX(escola.ano_censo)', 'end_year');
+  req.sql.from('escola_agregada')
+  .field('MIN(escola_agregada.ano_censo)', 'start_year')
+  .field('MAX(escola_agregada.ano_censo)', 'end_year');
   next();
 }, query, response('range'));
 
 classCountApp.get('/years', (req, res, next) => {
-  req.sql.from('escola')
-  .field('DISTINCT escola.ano_censo', 'year');
+  req.sql.from('escola_agregada')
+  .field('DISTINCT escola_agregada.ano_censo', 'year');
   next();
 }, query, response('years'));
 
diff --git a/src/libs/routes_v2/dailyChargeAmount.js b/src/libs/routes_v2/dailyChargeAmount.js
index 4af6cb7d..78115e48 100644
--- a/src/libs/routes_v2/dailyChargeAmount.js
+++ b/src/libs/routes_v2/dailyChargeAmount.js
@@ -207,7 +207,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: ['nome_escola', 'id'],
     resultField: ['school_name', 'school_id'],
     where: {
-- 
GitLab


From a371083b0f95666bf094523e3abbcb75e0a41ac9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 7 Mar 2023 10:49:56 -0300
Subject: [PATCH 062/123] Change infrastructure to use new table

---
 src/libs/routes_v2/infrastructure.js | 130 +++++++++++++--------------
 1 file changed, 65 insertions(+), 65 deletions(-)

diff --git a/src/libs/routes_v2/infrastructure.js b/src/libs/routes_v2/infrastructure.js
index 1ececa91..cc4c5931 100644
--- a/src/libs/routes_v2/infrastructure.js
+++ b/src/libs/routes_v2/infrastructure.js
@@ -47,22 +47,22 @@ let rqf = new ReqQueryFields();
 infrastructureApp.use(cache('15 day'));
 
 infrastructureApp.get('/year_range', (req, res, next) => {
-    req.sql.from('escola')
-    .field('MIN(escola.ano_censo)', 'start_year')
-    .field('MAX(escola.ano_censo)', 'end_year');
+    req.sql.from('escola_agregada')
+    .field('MIN(escola_agregada.ano_censo)', 'start_year')
+    .field('MAX(escola_agregada.ano_censo)', 'end_year');
     next();
 }, query, response('range'));
 
 infrastructureApp.get('/years', (req, res, next) => {
-    req.sql.from('escola')
-    .field('DISTINCT escola.ano_censo', 'year');
+    req.sql.from('escola_agregada')
+    .field('DISTINCT escola_agregada.ano_censo', 'year');
     next();
 }, query, response('years'));
 
 infrastructureApp.get('/source', (req, res, next) => {
     req.sql.from('fonte')
     .field('fonte', 'source')
-    .where('tabela = \'escola\'');
+    .where('tabela = \'escola_agregada\'');
     next();
 }, query, response('source'));
 
@@ -125,12 +125,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'dims').addValueToField({
     name: 'city',
@@ -141,12 +141,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'filter').addValueToField({
     name: 'state',
@@ -157,12 +157,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'estado_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'estado_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'dims').addValueToField({
     name: 'state',
@@ -173,12 +173,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'estado_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'estado_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'filter').addValue({
     name: 'region',
@@ -193,11 +193,11 @@ rqf.addField({
     join: {
         primary: 'id',
         foreign: 'regiao_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localizacao_id',
     resultField: 'location_id',
     where: {
@@ -207,7 +207,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'rural_location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localidade_area_rural',
     resultField: 'rural_location_id',
     where: {
@@ -217,7 +217,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'adm_dependency',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_id',
     resultField: 'adm_dependency_id',
     where: {
@@ -227,7 +227,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'adm_dependency_detailed',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_priv',
     resultField: 'adm_dependency_detailed_id',
     where: {
@@ -237,7 +237,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'min_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -247,7 +247,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'max_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -302,44 +302,44 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
 
     // Local de funcionamento
     let allSchools = req.sql.clone();
-    allSchools.from('escola').field('COUNT(escola.id)', 'total')
+    allSchools.from('escola_agregada').field('COUNT(escola_agregada.id)', 'total')
     .field("'Brasil'", 'name')
-    .field('escola.ano_censo', 'year')
-    .group('escola.ano_censo')
-    .where('escola.situacao_de_funcionamento = 1')
-    .order('escola.ano_censo');
+    .field('escola_agregada.ano_censo', 'year')
+    .group('escola_agregada.ano_censo')
+    .where('escola_agregada.situacao_de_funcionamento = 1')
+    .order('escola_agregada.ano_censo');
     req.queryIndex.allSchools = req.querySet.push(allSchools) - 1;
 
     let schoolPlace = allSchools.clone();
-    schoolPlace.where('escola.func_predio_escolar = 1 AND escola.func_salas_empresa = 0 AND escola.func_templo_igreja = 0 AND escola.func_casa_professor = 0 AND escola.func_galpao = 0 AND escola.biblioteca = 1');
+    schoolPlace.where('escola_agregada.func_predio_escolar = 1 AND escola_agregada.func_salas_empresa = 0 AND escola_agregada.func_templo_igreja = 0 AND escola_agregada.func_casa_professor = 0 AND escola_agregada.func_galpao = 0 AND escola_agregada.biblioteca = 1');
     req.queryIndex.schoolPlace = req.querySet.push(schoolPlace) - 1;
 
     // Bibliotecas
     let allLibraries = allSchools.clone();
-    allLibraries.where('escola.func_predio_escolar = 1 AND escola.localizacao_id = 1');
+    allLibraries.where('escola_agregada.func_predio_escolar = 1 AND escola_agregada.localizacao_id = 1');
     req.queryIndex.allLibraries = req.querySet.push(allLibraries) - 1;
 
     let haveLibraries = allLibraries.clone();
-    haveLibraries.where('escola.biblioteca = 1');
+    haveLibraries.where('escola_agregada.biblioteca = 1');
     req.queryIndex.haveLibraries = req.querySet.push(haveLibraries) - 1;
 
     // Bibliotecas/Sala de leitura
     let allLibrariesReadingRoom = allSchools.clone();
-    allLibrariesReadingRoom.where('escola.func_predio_escolar = 1 AND escola.localizacao_id = 2');
+    allLibrariesReadingRoom.where('escola_agregada.func_predio_escolar = 1 AND escola_agregada.localizacao_id = 2');
     req.queryIndex.allLibrariesReadingRoom = req.querySet.push(allLibrariesReadingRoom) - 1;
 
     let haveLibrariesReadingRoom = allLibrariesReadingRoom.clone();
-    haveLibrariesReadingRoom.where('escola.sala_leitura = 1');
+    haveLibrariesReadingRoom.where('escola_agregada.sala_leitura = 1');
     req.queryIndex.haveLibrariesReadingRoom = req.querySet.push(haveLibrariesReadingRoom) - 1;
 
     // Laboratório de informática
     let allInfLab = allSchools.clone();
-    allInfLab.where('escola.func_predio_escolar = 1')
-    .where('escola.reg_fund_ai = 1 OR escola.reg_fund_af = 1 OR escola.reg_medio_medio = 1 OR escola.reg_medio_integrado = 1 OR escola.reg_medio_normal = 1 OR escola.ensino_eja_fund = 1 OR escola.ensino_eja_medio = 1 OR escola.ensino_eja_prof = 1');
+    allInfLab.where('escola_agregada.func_predio_escolar = 1')
+    .where('escola_agregada.reg_fund_ai = 1 OR escola_agregada.reg_fund_af = 1 OR escola_agregada.reg_medio_medio = 1 OR escola_agregada.reg_medio_integrado = 1 OR escola_agregada.reg_medio_normal = 1 OR escola_agregada.ensino_eja_fund = 1 OR escola_agregada.ensino_eja_medio = 1 OR escola_agregada.ensino_eja_prof = 1');
     req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1;
 
     let haveInfLab = allInfLab.clone();
-    haveInfLab.where('escola.lab_informatica = 1');
+    haveInfLab.where('escola_agregada.lab_informatica = 1');
     req.queryIndex.haveInfLab = req.querySet.push(haveInfLab) - 1;
 
     // Laboratório de ciências
@@ -347,175 +347,175 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1;
 
     let haveScienceLab = allScienceLab.clone();
-    haveScienceLab.where('escola.lab_ciencias = 1');
+    haveScienceLab.where('escola_agregada.lab_ciencias = 1');
     req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1;
 
     // Parque infantil
     let allKidsPark = allSchools.clone();
-    allKidsPark.where('escola.func_predio_escolar = 1')
-    .where('escola.reg_infantil_creche = 1 OR escola.reg_infantil_preescola = 1 OR escola.reg_fund_ai = 1 OR escola.esp_infantil_creche = 1 OR escola.esp_exclusiva_creche = 1 OR escola.reg_esp_exclusiva_fund_ai = 1');
+    allKidsPark.where('escola_agregada.func_predio_escolar = 1')
+    .where('escola_agregada.reg_infantil_creche = 1 OR escola_agregada.reg_infantil_preescola = 1 OR escola_agregada.reg_fund_ai = 1 OR escola_agregada.esp_infantil_creche = 1 OR escola_agregada.esp_exclusiva_creche = 1 OR escola_agregada.reg_esp_exclusiva_fund_ai = 1');
     req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1;
 
     let haveKidsPark = allKidsPark.clone();
-    haveKidsPark.where('escola.parque_infantil = 1');
+    haveKidsPark.where('escola_agregada.parque_infantil = 1');
     req.queryIndex.haveKidsPark = req.querySet.push(haveKidsPark) - 1;
 
     // Berçário
     let allCribs = allSchools.clone();
-    allCribs.where('escola.func_predio_escolar = 1')
-    .where('escola.reg_infantil_creche = 1 OR escola.esp_infantil_creche = 1');
+    allCribs.where('escola_agregada.func_predio_escolar = 1')
+    .where('escola_agregada.reg_infantil_creche = 1 OR escola_agregada.esp_infantil_creche = 1');
     req.queryIndex.allCribs = req.querySet.push(allCribs) - 1;
 
     let haveCribs = allCribs.clone();
-    haveCribs.where('escola.bercario = 1');
+    haveCribs.where('escola_agregada.bercario = 1');
     req.queryIndex.haveCribs = req.querySet.push(haveCribs) - 1;
 
     // Quadra
     let allSportsCourt = allScienceLab.clone();
-    allSportsCourt.where('escola.localizacao_id = 1');
+    allSportsCourt.where('escola_agregada.localizacao_id = 1');
     req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1;
 
     let haveSportsCourt = allSportsCourt.clone();
-    haveSportsCourt.where('escola.quadra_esportes = 1');
+    haveSportsCourt.where('escola_agregada.quadra_esportes = 1');
     req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1;
 
     // Quadra coberta
     req.queryIndex.allCoveredSportsCourt = req.queryIndex.allSportsCourt;
 
     let haveCoveredSportsCourt = allSportsCourt.clone();
-    haveCoveredSportsCourt.where('escola.quadra_esportes_coberta = 1');
+    haveCoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 1');
     req.queryIndex.haveCoveredSportsCourt = req.querySet.push(haveCoveredSportsCourt) - 1;
 
     // Quadra Descoberta
     let allUncoveredSportsCourt = allSportsCourt.clone();
-    allUncoveredSportsCourt.where('escola.quadra_esportes_coberta = 0');
+    allUncoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 0');
     req.queryIndex.allUncoveredSportsCourt = req.querySet.push(allUncoveredSportsCourt) - 1;
 
     let haveUncoveredSportsCourt = allUncoveredSportsCourt.clone();
-    haveUncoveredSportsCourt.where('escola.quadra_esportes_descoberta = 1');
+    haveUncoveredSportsCourt.where('escola_agregada.quadra_esportes_descoberta = 1');
     req.queryIndex.haveUncoveredSportsCourt = req.querySet.push(haveUncoveredSportsCourt) - 1;
 
     // Sala de direção
     let allDirectorRoom = allSchools.clone();
-    allDirectorRoom.where('escola.func_predio_escolar = 1 AND escola.localizacao_id = 1');
+    allDirectorRoom.where('escola_agregada.func_predio_escolar = 1 AND escola_agregada.localizacao_id = 1');
     req.queryIndex.allDirectorRoom = req.querySet.push(allDirectorRoom) - 1;
 
     let haveDirectorRoom = allDirectorRoom.clone();
-    haveDirectorRoom.where('escola.sala_diretoria = 1');
+    haveDirectorRoom.where('escola_agregada.sala_diretoria = 1');
     req.queryIndex.haveDirectorRoom = req.querySet.push(haveDirectorRoom) - 1;
 
     // Secretaria
     let allSecretary = allSchools.clone();
-    allSecretary.where('escola.func_predio_escolar = 1');
+    allSecretary.where('escola_agregada.func_predio_escolar = 1');
     req.queryIndex.allSecretary = req.querySet.push(allSecretary) - 1;
 
     let haveSecretary = allSecretary.clone();
-    haveSecretary.where('escola.secretaria = 1');
+    haveSecretary.where('escola_agregada.secretaria = 1');
     req.queryIndex.haveSecretary = req.querySet.push(haveSecretary) - 1;
 
     // Sala de professores
     req.queryIndex.allTeacherRoom = req.queryIndex.allSecretary;
 
     let haveTeacherRoom = allSecretary.clone();
-    haveTeacherRoom.where('escola.sala_professor = 1');
+    haveTeacherRoom.where('escola_agregada.sala_professor = 1');
     req.queryIndex.haveTeacherRoom = req.querySet.push(haveTeacherRoom) - 1;
 
     // Cozinha
     req.queryIndex.allKitchen = req.queryIndex.allSecretary;
 
     let haveKitchen = allSecretary.clone();
-    haveKitchen.where('escola.cozinha = 1');
+    haveKitchen.where('escola_agregada.cozinha = 1');
     req.queryIndex.haveKitchen = req.querySet.push(haveKitchen) - 1;
 
     // Despensa
     req.queryIndex.allStoreroom = req.queryIndex.allSecretary;
 
     let haveStoreroom = allSecretary.clone();
-    haveStoreroom.where('escola.despensa = 1');
+    haveStoreroom.where('escola_agregada.despensa = 1');
     req.queryIndex.haveStoreroom = req.querySet.push(haveStoreroom) - 1;
 
     // Almoxarifado
     req.queryIndex.allWarehouse = req.queryIndex.allSecretary;
 
     let haveWarehouse = allSecretary.clone();
-    haveWarehouse.where('escola.almoxarifado = 1');
+    haveWarehouse.where('escola_agregada.almoxarifado = 1');
     req.queryIndex.haveWarehouse = req.querySet.push(haveWarehouse) - 1;
 
     // Internet
     req.queryIndex.allInternet = req.queryIndex.allLibrariesReadingRoom;
 
     let haveInternet = allLibrariesReadingRoom.clone();
-    haveInternet.where('escola.internet = 1');
+    haveInternet.where('escola_agregada.internet = 1');
     req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1;
 
     // Internet banda larga
     req.queryIndex.allBroadbandInternet = req.queryIndex.allLibraries;
 
     let haveBroadbandInternet = allLibraries.clone();
-    haveBroadbandInternet.where('escola.internet_banda_larga = 1');
+    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1');
     req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1;
 
     // Banheiro dentro do prédio
     req.queryIndex.allInsideBathroom = req.queryIndex.allSecretary;
 
     let haveInsideBathroom = allSecretary.clone();
-    haveInsideBathroom.where('escola.sanitario_dentro_predio = 1');
+    haveInsideBathroom.where('escola_agregada.sanitario_dentro_predio = 1');
     req.queryIndex.haveInsideBathroom = req.querySet.push(haveInsideBathroom) - 1;
 
     // Banheiro adequado para educação infantil dentro do prédio
     req.queryIndex.allInsideKidsBathroom = req.queryIndex.allKidsPark;
 
     let haveInsideKidsBathroom = allKidsPark.clone();
-    haveInsideKidsBathroom.where('escola.sanitario_ei = 1');
+    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1');
     req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1;
 
     // Fornecimento de energia
     req.queryIndex.allEletricEnergy = req.queryIndex.allSecretary;
 
     let haveEletricEnergy = allSecretary.clone();
-    haveEletricEnergy.where('escola.fornecimento_energia = 1');
+    haveEletricEnergy.where('escola_agregada.fornecimento_energia = 1');
     req.queryIndex.haveEletricEnergy = req.querySet.push(haveEletricEnergy) - 1;
 
     // Abastecimento de água
     req.queryIndex.allWaterSupply = req.queryIndex.allSecretary;
 
     let haveWaterSupply = allSecretary.clone();
-    haveWaterSupply.where('escola.fornecimento_agua = 1');
+    haveWaterSupply.where('escola_agregada.fornecimento_agua = 1');
     req.queryIndex.haveWaterSupply = req.querySet.push(haveWaterSupply) - 1;
 
     // Água filtrada
     req.queryIndex.allFilteredWater = req.queryIndex.allSecretary;
 
     let haveFilteredWater = allSecretary.clone();
-    haveFilteredWater.where('escola.agua_filtrada = 1');
+    haveFilteredWater.where('escola_agregada.agua_filtrada = 1');
     req.queryIndex.haveFilteredWater = req.querySet.push(haveFilteredWater) - 1;
 
     // Coleta de esgoto
     req.queryIndex.allSewage = req.queryIndex.allSecretary;
 
     let haveSewage = allSecretary.clone();
-    haveSewage.where('escola.esgoto_sanitario = 1');
+    haveSewage.where('escola_agregada.esgoto_sanitario = 1');
     req.queryIndex.haveSewage = req.querySet.push(haveSewage) - 1;
 
     // Sala de recursos multifuncionais para Atendimento Educacional Especializado
     req.queryIndex.allMultifunctionRoom = req.queryIndex.allSecretary;
 
     let haveMultifunctionRoom = allSecretary.clone();
-    haveMultifunctionRoom.where('escola.sala_atendimento_especial = 1');
+    haveMultifunctionRoom.where('escola_agregada.sala_atendimento_especial = 1');
     req.queryIndex.haveMultifunctionRoom = req.querySet.push(haveMultifunctionRoom) - 1;
 
     // Banheiros adaptados para pessoas com deficiências
     req.queryIndex.allSpecialBathroom = req.queryIndex.allSecretary;
 
     let haveSpecialBathroom = allSecretary.clone();
-    haveSpecialBathroom.where('escola.sanitario_pne = 1');
+    haveSpecialBathroom.where('escola_agregada.sanitario_pne = 1');
     req.queryIndex.haveSpecialBathroom = req.querySet.push(haveSpecialBathroom) - 1;
 
     // Dependências adaptada para pessoas com deficiências
     req.queryIndex.allAdaptedBuilding = req.queryIndex.allSecretary;
 
     let haveAdaptedBuilding = allSecretary.clone();
-    haveAdaptedBuilding.where('escola.dependencias_pne = 1');
+    haveAdaptedBuilding.where('escola_agregada.dependencias_pne = 1');
     req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1;
 
     next();
-- 
GitLab


From 35cf2b8631f80ef06574f471471b603cee64d6cf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 7 Mar 2023 11:10:24 -0300
Subject: [PATCH 063/123] Change class to use new table

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

diff --git a/src/libs/routes_v2/class.js b/src/libs/routes_v2/class.js
index 4eca8cc3..326bcf18 100644
--- a/src/libs/routes_v2/class.js
+++ b/src/libs/routes_v2/class.js
@@ -346,7 +346,7 @@ rqfCount.addField({
   }
 }).addValueToField({
     name: 'school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: ['nome_escola', 'id'],
     resultField: ['school_name', 'school_id'],
     where: {
@@ -361,7 +361,7 @@ rqfCount.addField({
     }
 }, 'dims').addValueToField({
     name: 'school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'nome_escola',
     resultField: 'school_name',
     where: {
-- 
GitLab


From b0b494acd7097806e2ba584a68c5de8d776c9d26 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 23 Mar 2023 11:32:42 -0300
Subject: [PATCH 064/123] Create first report route

---
 src/libs/routes_v2/api.js               |   3 +
 src/libs/routes_v2/simcaqFirstReport.js | 136 ++++++++++++++++++++++++
 2 files changed, 139 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqFirstReport.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 906af9cf..5365e80e 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -134,6 +134,8 @@ const message = require(`${libs}/routes_v2/message`);
 
 const courseStudents = require(`${libs}/routes_v2/courseStudents`);
 
+const simcaqFirstReport = require(`${libs}/routes_v2/simcaqFirstReport`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -192,5 +194,6 @@ api.use('/disciplines', disciplines);
 api.use('/universityLocalOffer', universityLocalOffer);
 api.use('/message', message);
 api.use('/course_students', courseStudents);
+api.use('/simcaq_first_report', simcaqFirstReport);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
new file mode 100644
index 00000000..db300da5
--- /dev/null
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -0,0 +1,136 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqFirstReportApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+simcaqFirstReportApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'year',
+    table: 'simcaq_relatorio_1',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'simcaq_relatorio_1',
+    tableField: 'dependencia_adm_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'city',
+    table: 'simcaq_relatorio_1',
+    tableField: 'municipio_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id'
+    }
+}).addValue({
+    name: 'state',
+    table: 'simcaq_relatorio_1',
+    tableField: 'estado_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id'
+    }
+}).addValue({
+    name: 'school',
+    table: 'simcaq_relatorio_1',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'location',
+    table: 'simcaq_relatorio_1',
+    tableField: 'localizacao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'stage',
+    table: 'simcaq_relatorio_1',
+    tableField: 'etapa',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa'
+    }
+}).addValue({
+    name: 'shift',
+    table: 'simcaq_relatorio_1',
+    tableField: 'turno',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'turno'
+    }
+});
+
+simcaqFirstReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_relatorio_1')
+        .field('simcaq_relatorio_1.etapa', 'stage')
+        .field('simcaq_relatorio_1.turno', 'shift')
+        .field('simcaq_relatorio_1.localizacao_id', 'location')
+        .field('SUM(simcaq_relatorio_1.num_matriculas)', 'num_enrollments')
+        .field('SUM(simcaq_relatorio_1.num_escolas)', 'num_schools')
+        .group('simcaq_relatorio_1.etapa')
+        .group('simcaq_relatorio_1.turno')
+        .group('simcaq_relatorio_1.localizacao_id');
+    next();
+}, query, response('simcaqFirstReport'));
+
+module.exports = simcaqFirstReportApp;
-- 
GitLab


From 9698bb016b7d6fa9bd168d46b393909a3053f0d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 24 Mar 2023 11:48:03 -0300
Subject: [PATCH 065/123] Add enrollment diagnosis route

---
 src/libs/routes_v2/api.js                  |   3 +
 src/libs/routes_v2/enrollment_diagnosis.js | 133 +++++++++++++++++++++
 2 files changed, 136 insertions(+)
 create mode 100644 src/libs/routes_v2/enrollment_diagnosis.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 5365e80e..071f8ffa 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -136,6 +136,8 @@ const courseStudents = require(`${libs}/routes_v2/courseStudents`);
 
 const simcaqFirstReport = require(`${libs}/routes_v2/simcaqFirstReport`);
 
+const enrollmentDiagnosis = require(`${libs}/routes_v2/enrollment_diagnosis`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -195,5 +197,6 @@ api.use('/universityLocalOffer', universityLocalOffer);
 api.use('/message', message);
 api.use('/course_students', courseStudents);
 api.use('/simcaq_first_report', simcaqFirstReport);
+api.use('/enrollment_diagnosis', enrollmentDiagnosis);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/enrollment_diagnosis.js b/src/libs/routes_v2/enrollment_diagnosis.js
new file mode 100644
index 00000000..221549e5
--- /dev/null
+++ b/src/libs/routes_v2/enrollment_diagnosis.js
@@ -0,0 +1,133 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const enrollmentDiagnosisApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+enrollmentDiagnosisApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'year',
+    table: 'simcaq_diagnostico_de_matricula',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'simcaq_diagnostico_de_matricula',
+    tableField: 'municipio_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id'
+    }
+}).addValue({
+    name: 'state',
+    table: 'simcaq_diagnostico_de_matricula',
+    tableField: 'estado_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id'
+    }
+}).addValue({
+    name: 'school',
+    table: 'simcaq_diagnostico_de_matricula',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'stage',
+    table: 'simcaq_diagnostico_de_matricula',
+    tableField: 'etapa',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa'
+    }
+}).addValue({
+    name: 'location',
+    table: 'simcaq_diagnostico_de_matricula',
+    tableField: 'localizacao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'simcaq_diagnostico_de_matricula',
+    tableField: 'dependencia_adm_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+});
+
+enrollmentDiagnosisApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_diagnostico_de_matricula')
+        .field('simcaq_diagnostico_de_matricula.etapa', 'stage')
+        .field('SUM(simcaq_diagnostico_de_matricula.total)', 'total')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_federal)', 'federal_enrollments')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_estadual)', 'state_enrollments')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_municipal)', 'city_enrollments')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_priv_conv_sem_fin)', 'private_affiliated_non_profit_enrollments')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_priv_conv_com_fin)', 'private_affiliated_for_profit_enrollments')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_priv_nao_conv_sem_fin)', 'private_not_affiliated_non_profit_enrollments')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_priv_nao_conv_com_fin)', 'private_not_affiliated_for_profit_enrollments')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_urbana)', 'urban_enrollments')
+        .field('SUM(simcaq_diagnostico_de_matricula.matriculas_rural)', 'rural_enrollments')
+        .group('simcaq_diagnostico_de_matricula.etapa');
+
+    console.log(req.sql.toString());
+    next();
+}, query, response('enrollmentDiagnosis'));
+
+module.exports = enrollmentDiagnosisApp;
-- 
GitLab


From 64a3d1b2ea3f4982dbd503885014b897a456eeab Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Mon, 27 Mar 2023 09:59:40 -0300
Subject: [PATCH 066/123] simcaq second report route added and some fixes

---
 src/libs/routes_v2/api.js                |   3 +
 src/libs/routes_v2/simcaqFirstReport.js  |   1 +
 src/libs/routes_v2/simcaqSecondReport.js | 110 +++++++++++++++++++++++
 3 files changed, 114 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqSecondReport.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 071f8ffa..cb19b9a4 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -138,6 +138,8 @@ const simcaqFirstReport = require(`${libs}/routes_v2/simcaqFirstReport`);
 
 const enrollmentDiagnosis = require(`${libs}/routes_v2/enrollment_diagnosis`);
 
+const simcaqSecondReport = require(`${libs}/routes_v2/simcaqSecondReport`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -198,5 +200,6 @@ api.use('/message', message);
 api.use('/course_students', courseStudents);
 api.use('/simcaq_first_report', simcaqFirstReport);
 api.use('/enrollment_diagnosis', enrollmentDiagnosis);
+api.use('/simcaq_second_report', simcaqSecondReport);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
index db300da5..c4c4b978 100644
--- a/src/libs/routes_v2/simcaqFirstReport.js
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -127,6 +127,7 @@ simcaqFirstReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('simcaq_relatorio_1.localizacao_id', 'location')
         .field('SUM(simcaq_relatorio_1.num_matriculas)', 'num_enrollments')
         .field('SUM(simcaq_relatorio_1.num_escolas)', 'num_schools')
+        .where('situacao_funcionamento_pareada = 1 AND (ensino_regular = 1 OR ensino_eja = 1 OR educacao_profissional = 1)')
         .group('simcaq_relatorio_1.etapa')
         .group('simcaq_relatorio_1.turno')
         .group('simcaq_relatorio_1.localizacao_id');
diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
new file mode 100644
index 00000000..51907e33
--- /dev/null
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -0,0 +1,110 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqSecondReportApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+simcaqSecondReportApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addValue({
+    name: 'year',
+    table: 'simcaq_relatorio_2',
+    tableField: 'ano_censo',
+    where : {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'school',
+    table: 'simcaq_relatorio_2',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    }
+}).addValue({
+    name: 'city',
+    table:'simcaq_relatorio_2',
+    tableField: 'municipio_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'municipio_id'
+    }
+}).addValue({
+    name: 'adm_dependency',
+    table: 'simcaq_relatorio_2',
+    tableField: 'dependencia_adm_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'state',
+    table: 'simcaq_relatorio_2',
+    tableField: 'estado_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'estado_id'
+    }
+});
+
+simcaqSecondReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_relatorio_2')
+        .field('sum(num_matriculas_total)' ,'num_enrollments_total')
+        .field('sum(num_matriculas_rural)' ,'num_enrollments_rural')
+        .field('sum(num_matriculas_urbana)' ,'num_enrollments_urban')
+        .field('sum(num_escolas_total)' ,'num_schools_total')
+        .field('sum(num_escolas_rural)' ,'num_schools_rural')
+        .field('sum(num_escolas_urbana)' ,'num_schools_urban')
+        .field('dependencia_adm_priv', 'private_adm_dependency')
+        .where('situacao_funcionamento_pareada = 1 AND (ensino_regular = 1 OR ensino_eja = 1 OR educacao_profissional = 1)')
+        .group('dependencia_adm_priv')
+    next();
+}, query, response('simcaqSecondReport'));
+
+module.exports = simcaqSecondReportApp;
-- 
GitLab


From fd0274da876fd97cddd493b3a4bc1f0c48de7872 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 27 Mar 2023 11:10:07 -0300
Subject: [PATCH 067/123] Fix issue with private_adm_dependency

---
 src/libs/routes_v2/simcaqFirstReport.js  | 1 -
 src/libs/routes_v2/simcaqSecondReport.js | 3 +--
 2 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
index c4c4b978..db300da5 100644
--- a/src/libs/routes_v2/simcaqFirstReport.js
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -127,7 +127,6 @@ simcaqFirstReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('simcaq_relatorio_1.localizacao_id', 'location')
         .field('SUM(simcaq_relatorio_1.num_matriculas)', 'num_enrollments')
         .field('SUM(simcaq_relatorio_1.num_escolas)', 'num_schools')
-        .where('situacao_funcionamento_pareada = 1 AND (ensino_regular = 1 OR ensino_eja = 1 OR educacao_profissional = 1)')
         .group('simcaq_relatorio_1.etapa')
         .group('simcaq_relatorio_1.turno')
         .group('simcaq_relatorio_1.localizacao_id');
diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
index 51907e33..6babe383 100644
--- a/src/libs/routes_v2/simcaqSecondReport.js
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -102,8 +102,7 @@ simcaqSecondReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('sum(num_escolas_rural)' ,'num_schools_rural')
         .field('sum(num_escolas_urbana)' ,'num_schools_urban')
         .field('dependencia_adm_priv', 'private_adm_dependency')
-        .where('situacao_funcionamento_pareada = 1 AND (ensino_regular = 1 OR ensino_eja = 1 OR educacao_profissional = 1)')
-        .group('dependencia_adm_priv')
+        .group('dependencia_adm_priv');
     next();
 }, query, response('simcaqSecondReport'));
 
-- 
GitLab


From 9c7176ac43d91d9b2c4764db337b6cd9afada9eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 27 Mar 2023 11:23:58 -0300
Subject: [PATCH 068/123] Fix routes to use escola_id field instead of id,
 removed unnecessary console logs

---
 src/libs/routes_v2/api.js                          |  4 ++--
 ...t_diagnosis.js => simcaqEnrollmentDiagnosis.js} | 14 ++++++--------
 src/libs/routes_v2/simcaqFirstReport.js            |  4 ++--
 src/libs/routes_v2/simcaqSecondReport.js           |  4 ++--
 4 files changed, 12 insertions(+), 14 deletions(-)
 rename src/libs/routes_v2/{enrollment_diagnosis.js => simcaqEnrollmentDiagnosis.js} (93%)

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index cb19b9a4..f6804584 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -136,7 +136,7 @@ const courseStudents = require(`${libs}/routes_v2/courseStudents`);
 
 const simcaqFirstReport = require(`${libs}/routes_v2/simcaqFirstReport`);
 
-const enrollmentDiagnosis = require(`${libs}/routes_v2/enrollment_diagnosis`);
+const simcaqEnrollmentDiagnosis = require(`${libs}/routes_v2/simcaqEnrollmentDiagnosis`);
 
 const simcaqSecondReport = require(`${libs}/routes_v2/simcaqSecondReport`);
 
@@ -199,7 +199,7 @@ api.use('/universityLocalOffer', universityLocalOffer);
 api.use('/message', message);
 api.use('/course_students', courseStudents);
 api.use('/simcaq_first_report', simcaqFirstReport);
-api.use('/enrollment_diagnosis', enrollmentDiagnosis);
+api.use('/simcaq_enrollment_diagnosis', simcaqEnrollmentDiagnosis);
 api.use('/simcaq_second_report', simcaqSecondReport);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/enrollment_diagnosis.js b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
similarity index 93%
rename from src/libs/routes_v2/enrollment_diagnosis.js
rename to src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
index 221549e5..bd23cf25 100644
--- a/src/libs/routes_v2/enrollment_diagnosis.js
+++ b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
@@ -20,7 +20,7 @@ along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
 
 const express = require('express');
 
-const enrollmentDiagnosisApp = express.Router();
+const simcaqEnrollmentDiagnosisApp = express.Router();
 
 const libs = `${process.cwd()}/libs`;
 
@@ -40,7 +40,7 @@ const cache = require('apicache').options({ debug: config.debug, statusCodes: {i
 
 let rqf = new ReqQueryFields();
 
-enrollmentDiagnosisApp.use(cache('15 day'));
+simcaqEnrollmentDiagnosisApp.use(cache('15 day'));
 
 rqf.addField({
     name: 'filter',
@@ -76,11 +76,11 @@ rqf.addField({
 }).addValue({
     name: 'school',
     table: 'simcaq_diagnostico_de_matricula',
-    tableField: 'id',
+    tableField: 'escola_id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'id'
+        field: 'escola_id'
     }
 }).addValue({
     name: 'stage',
@@ -111,7 +111,7 @@ rqf.addField({
     }
 });
 
-enrollmentDiagnosisApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+simcaqEnrollmentDiagnosisApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.sql.from('simcaq_diagnostico_de_matricula')
         .field('simcaq_diagnostico_de_matricula.etapa', 'stage')
         .field('SUM(simcaq_diagnostico_de_matricula.total)', 'total')
@@ -125,9 +125,7 @@ enrollmentDiagnosisApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('SUM(simcaq_diagnostico_de_matricula.matriculas_urbana)', 'urban_enrollments')
         .field('SUM(simcaq_diagnostico_de_matricula.matriculas_rural)', 'rural_enrollments')
         .group('simcaq_diagnostico_de_matricula.etapa');
-
-    console.log(req.sql.toString());
     next();
 }, query, response('enrollmentDiagnosis'));
 
-module.exports = enrollmentDiagnosisApp;
+module.exports = simcaqEnrollmentDiagnosisApp;
diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
index db300da5..b6bde164 100644
--- a/src/libs/routes_v2/simcaqFirstReport.js
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -85,11 +85,11 @@ rqf.addField({
 }).addValue({
     name: 'school',
     table: 'simcaq_relatorio_1',
-    tableField: 'id',
+    tableField: 'escola_id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'id'
+        field: 'escola_id'
     }
 }).addValue({
     name: 'location',
diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
index 6babe383..fdd583d6 100644
--- a/src/libs/routes_v2/simcaqSecondReport.js
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -58,11 +58,11 @@ rqf.addField({
 }).addValue({
     name: 'school',
     table: 'simcaq_relatorio_2',
-    tableField: 'id',
+    tableField: 'escola_id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'id'
+        field: 'escola_id'
     }
 }).addValue({
     name: 'city',
-- 
GitLab


From 94f3e0e818db144c46ef0fa1e4b2a648bf5c8f47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 27 Mar 2023 11:37:40 -0300
Subject: [PATCH 069/123] add year field to new routes default queries

---
 src/libs/routes_v2/simcaqEnrollmentDiagnosis.js | 6 ++++--
 src/libs/routes_v2/simcaqFirstReport.js         | 6 ++++--
 src/libs/routes_v2/simcaqSecondReport.js        | 2 ++
 3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
index bd23cf25..7c13d9b4 100644
--- a/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
+++ b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
@@ -83,7 +83,7 @@ rqf.addField({
         field: 'escola_id'
     }
 }).addValue({
-    name: 'stage',
+    name: 'education_level_short_id',
     table: 'simcaq_diagnostico_de_matricula',
     tableField: 'etapa',
     where: {
@@ -113,7 +113,7 @@ rqf.addField({
 
 simcaqEnrollmentDiagnosisApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.sql.from('simcaq_diagnostico_de_matricula')
-        .field('simcaq_diagnostico_de_matricula.etapa', 'stage')
+        .field('simcaq_diagnostico_de_matricula.etapa', 'education_level_short_id')
         .field('SUM(simcaq_diagnostico_de_matricula.total)', 'total')
         .field('SUM(simcaq_diagnostico_de_matricula.matriculas_federal)', 'federal_enrollments')
         .field('SUM(simcaq_diagnostico_de_matricula.matriculas_estadual)', 'state_enrollments')
@@ -124,6 +124,8 @@ simcaqEnrollmentDiagnosisApp.get('/', rqf.parse(), rqf.build(), (req, res, next)
         .field('SUM(simcaq_diagnostico_de_matricula.matriculas_priv_nao_conv_com_fin)', 'private_not_affiliated_for_profit_enrollments')
         .field('SUM(simcaq_diagnostico_de_matricula.matriculas_urbana)', 'urban_enrollments')
         .field('SUM(simcaq_diagnostico_de_matricula.matriculas_rural)', 'rural_enrollments')
+        .field('ano_censo', 'year')
+        .group('ano_censo')
         .group('simcaq_diagnostico_de_matricula.etapa');
     next();
 }, query, response('enrollmentDiagnosis'));
diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
index b6bde164..b3bca02b 100644
--- a/src/libs/routes_v2/simcaqFirstReport.js
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -101,7 +101,7 @@ rqf.addField({
         field: 'localizacao_id'
     }
 }).addValue({
-    name: 'stage',
+    name: 'education_level_short_id',
     table: 'simcaq_relatorio_1',
     tableField: 'etapa',
     where: {
@@ -122,11 +122,13 @@ rqf.addField({
 
 simcaqFirstReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.sql.from('simcaq_relatorio_1')
-        .field('simcaq_relatorio_1.etapa', 'stage')
+        .field('simcaq_relatorio_1.etapa', 'education_level_short_id')
         .field('simcaq_relatorio_1.turno', 'shift')
         .field('simcaq_relatorio_1.localizacao_id', 'location')
         .field('SUM(simcaq_relatorio_1.num_matriculas)', 'num_enrollments')
         .field('SUM(simcaq_relatorio_1.num_escolas)', 'num_schools')
+        .field('ano_censo', 'year')
+        .group('ano_censo')
         .group('simcaq_relatorio_1.etapa')
         .group('simcaq_relatorio_1.turno')
         .group('simcaq_relatorio_1.localizacao_id');
diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
index fdd583d6..1aa89412 100644
--- a/src/libs/routes_v2/simcaqSecondReport.js
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -102,6 +102,8 @@ simcaqSecondReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('sum(num_escolas_rural)' ,'num_schools_rural')
         .field('sum(num_escolas_urbana)' ,'num_schools_urban')
         .field('dependencia_adm_priv', 'private_adm_dependency')
+        .field('ano_censo', 'year')
+        .group('ano_censo')
         .group('dependencia_adm_priv');
     next();
 }, query, response('simcaqSecondReport'));
-- 
GitLab


From 2a51e27ecc5e487c1c1d4a5205a4d2f0e25f3a1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 28 Mar 2023 09:48:11 -0300
Subject: [PATCH 070/123] Add shift converter

---
 src/libs/convert/shift.js               | 30 +++++++++++++++++++++++++
 src/libs/middlewares/id2str.js          |  7 ++++--
 src/libs/routes_v2/simcaqFirstReport.js | 13 ++++++++---
 3 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 src/libs/convert/shift.js

diff --git a/src/libs/convert/shift.js b/src/libs/convert/shift.js
new file mode 100644
index 00000000..effe0a52
--- /dev/null
+++ b/src/libs/convert/shift.js
@@ -0,0 +1,30 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+module.exports = function shift(id) {
+    switch(id) {
+        case 1:
+            return 'Parcial';
+        case 2:
+            return 'Integral';
+        default:
+            return 'Não especificado';
+    }
+}
\ No newline at end of file
diff --git a/src/libs/middlewares/id2str.js b/src/libs/middlewares/id2str.js
index bd5c83c0..b59f6463 100644
--- a/src/libs/middlewares/id2str.js
+++ b/src/libs/middlewares/id2str.js
@@ -95,6 +95,7 @@ const enrollmentSituation = require(`${libs}/convert/enrollmentSituation`);
 const diffLocation = require(`${libs}/convert/diffLocation`);
 const peePorCategoria = require(`${libs}/convert/peePorCategoria`);
 const pee = require(`${libs}/convert/booleanVariable`);
+const shift = require(`${libs}/convert/shift`);
 
 const ids = {
     gender_id: gender,
@@ -181,7 +182,8 @@ const ids = {
     enrollment_situation: enrollmentSituation,
     diff_location_id: diffLocation,
     pee_por_categoria: peePorCategoria,
-    pee_id: pee
+    pee_id: pee,
+    shift_id: shift
 };
 
 function transform(removeId=false) {
@@ -295,5 +297,6 @@ module.exports = {
     enrollmentSituation,
     diffLocation,
     peePorCategoria,
-    pee
+    pee,
+    shift
 };
diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
index b3bca02b..7f99fbd6 100644
--- a/src/libs/routes_v2/simcaqFirstReport.js
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -40,6 +40,8 @@ const cache = require('apicache').options({ debug: config.debug, statusCodes: {i
 
 let rqf = new ReqQueryFields();
 
+const id2str = require(`${libs}/middlewares/id2str`);
+
 simcaqFirstReportApp.use(cache('15 day'));
 
 rqf.addField({
@@ -110,7 +112,7 @@ rqf.addField({
         field: 'etapa'
     }
 }).addValue({
-    name: 'shift',
+    name: 'shift_id',
     table: 'simcaq_relatorio_1',
     tableField: 'turno',
     where: {
@@ -123,7 +125,7 @@ rqf.addField({
 simcaqFirstReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.sql.from('simcaq_relatorio_1')
         .field('simcaq_relatorio_1.etapa', 'education_level_short_id')
-        .field('simcaq_relatorio_1.turno', 'shift')
+        .field('simcaq_relatorio_1.turno', 'shift_id')
         .field('simcaq_relatorio_1.localizacao_id', 'location')
         .field('SUM(simcaq_relatorio_1.num_matriculas)', 'num_enrollments')
         .field('SUM(simcaq_relatorio_1.num_escolas)', 'num_schools')
@@ -133,6 +135,11 @@ simcaqFirstReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .group('simcaq_relatorio_1.turno')
         .group('simcaq_relatorio_1.localizacao_id');
     next();
-}, query, response('simcaqFirstReport'));
+}, query, (req, res, next) => {
+    req.result.forEach((result) => {
+        result.shift = id2str.shift(result.shift_id);
+    });
+    next();
+}, response('simcaqFirstReport'));
 
 module.exports = simcaqFirstReportApp;
-- 
GitLab


From 0d7b853a2ad2ef97a70c3ab1dd6d379e486d16ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 28 Mar 2023 10:38:01 -0300
Subject: [PATCH 071/123] Add dimensions to first report route

---
 src/libs/routes_v2/simcaqFirstReport.js | 43 ++++++++++++++++++-------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
index 7f99fbd6..16640eee 100644
--- a/src/libs/routes_v2/simcaqFirstReport.js
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -48,6 +48,10 @@ rqf.addField({
     name: 'filter',
     field: false,
     where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
 }).addValue({
     name: 'year',
     table: 'simcaq_relatorio_1',
@@ -68,32 +72,47 @@ rqf.addField({
     }
 }).addValue({
     name: 'city',
-    table: 'simcaq_relatorio_1',
-    tableField: 'municipio_id',
+    table: 'municipio',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'municipio_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_relatorio_1'
     }
-}).addValue({
+}, 'dims').addValue({
     name: 'state',
-    table: 'simcaq_relatorio_1',
-    tableField: 'estado_id',
+    table: 'estado',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'estado_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_relatorio_1'
     }
-}).addValue({
+}, 'dims').addValue({
     name: 'school',
-    table: 'simcaq_relatorio_1',
-    tableField: 'escola_id',
+    table: 'escola_agregada',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'escola_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_relatorio_1'
     }
-}).addValue({
+}, 'dims').addValue({
     name: 'location',
     table: 'simcaq_relatorio_1',
     tableField: 'localizacao_id',
-- 
GitLab


From b993f5cd9cddee5a576d03e655af6ed582d0d9c8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 28 Mar 2023 10:44:51 -0300
Subject: [PATCH 072/123] Add dims to second report and enrollment diagnosis
 routes

---
 .../routes_v2/simcaqEnrollmentDiagnosis.js    | 43 +++++++++++----
 src/libs/routes_v2/simcaqSecondReport.js      | 55 +++++++++++++------
 2 files changed, 68 insertions(+), 30 deletions(-)

diff --git a/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
index 7c13d9b4..1fb30b61 100644
--- a/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
+++ b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
@@ -46,6 +46,10 @@ rqf.addField({
     name: 'filter',
     field: false,
     where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
 }).addValue({
     name: 'year',
     table: 'simcaq_diagnostico_de_matricula',
@@ -57,32 +61,47 @@ rqf.addField({
     }
 }).addValue({
     name: 'city',
-    table: 'simcaq_diagnostico_de_matricula',
-    tableField: 'municipio_id',
+    table: 'municipio',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'municipio_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_diagnostico_de_matricula'
     }
-}).addValue({
+}, 'dims').addValue({
     name: 'state',
-    table: 'simcaq_diagnostico_de_matricula',
-    tableField: 'estado_id',
+    table: 'estado',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'estado_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_diagnostico_de_matricula'
     }
-}).addValue({
+}, 'dims').addValue({
     name: 'school',
-    table: 'simcaq_diagnostico_de_matricula',
-    tableField: 'escola_id',
+    table: 'escola_agregada',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'escola_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_diagnostico_de_matricula'
     }
-}).addValue({
+}, 'dims').addValue({
     name: 'education_level_short_id',
     table: 'simcaq_diagnostico_de_matricula',
     tableField: 'etapa',
diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
index 1aa89412..ccdd2f9b 100644
--- a/src/libs/routes_v2/simcaqSecondReport.js
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -46,6 +46,10 @@ rqf.addField({
     name: 'filter',
     field: false,
     where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
 }).addValue({
     name: 'year',
     table: 'simcaq_relatorio_2',
@@ -56,40 +60,55 @@ rqf.addField({
         field: 'ano_censo'
     }
 }).addValue({
-    name: 'school',
-    table: 'simcaq_relatorio_2',
-    tableField: 'escola_id',
+    name: 'city',
+    table: 'municipio',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'escola_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_relatorio_2'
     }
-}).addValue({
-    name: 'city',
-    table:'simcaq_relatorio_2',
-    tableField: 'municipio_id',
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'municipio_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_relatorio_2'
     }
-}).addValue({
-    name: 'adm_dependency',
-    table: 'simcaq_relatorio_2',
-    tableField: 'dependencia_adm_id',
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'dependencia_adm_id'
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_relatorio_2'
     }
-}).addValue({
-    name: 'state',
+}, 'dims').addValue({
+    name: 'adm_dependency',
     table: 'simcaq_relatorio_2',
-    tableField: 'estado_id',
+    tableField: 'dependencia_adm_id',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'estado_id'
+        field: 'dependencia_adm_id'
     }
 });
 
-- 
GitLab


From c372fbacadf1a5161aa9d3b4201041bade07acc3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 28 Mar 2023 11:37:11 -0300
Subject: [PATCH 073/123] Fix issue when calling id2str methods

---
 src/libs/routes_v2/simcaqEnrollmentDiagnosis.js | 4 +++-
 src/libs/routes_v2/simcaqFirstReport.js         | 9 ++-------
 src/libs/routes_v2/simcaqSecondReport.js        | 4 +++-
 3 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
index 1fb30b61..d77aa191 100644
--- a/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
+++ b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
@@ -40,6 +40,8 @@ const cache = require('apicache').options({ debug: config.debug, statusCodes: {i
 
 let rqf = new ReqQueryFields();
 
+const id2str = require(`${libs}/middlewares/id2str`);
+
 simcaqEnrollmentDiagnosisApp.use(cache('15 day'));
 
 rqf.addField({
@@ -147,6 +149,6 @@ simcaqEnrollmentDiagnosisApp.get('/', rqf.parse(), rqf.build(), (req, res, next)
         .group('ano_censo')
         .group('simcaq_diagnostico_de_matricula.etapa');
     next();
-}, query, response('enrollmentDiagnosis'));
+}, query, id2str.transform(), response('enrollmentDiagnosis'));
 
 module.exports = simcaqEnrollmentDiagnosisApp;
diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
index 16640eee..d8e3cf0f 100644
--- a/src/libs/routes_v2/simcaqFirstReport.js
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -145,7 +145,7 @@ simcaqFirstReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.sql.from('simcaq_relatorio_1')
         .field('simcaq_relatorio_1.etapa', 'education_level_short_id')
         .field('simcaq_relatorio_1.turno', 'shift_id')
-        .field('simcaq_relatorio_1.localizacao_id', 'location')
+        .field('simcaq_relatorio_1.localizacao_id', 'location_id')
         .field('SUM(simcaq_relatorio_1.num_matriculas)', 'num_enrollments')
         .field('SUM(simcaq_relatorio_1.num_escolas)', 'num_schools')
         .field('ano_censo', 'year')
@@ -154,11 +154,6 @@ simcaqFirstReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .group('simcaq_relatorio_1.turno')
         .group('simcaq_relatorio_1.localizacao_id');
     next();
-}, query, (req, res, next) => {
-    req.result.forEach((result) => {
-        result.shift = id2str.shift(result.shift_id);
-    });
-    next();
-}, response('simcaqFirstReport'));
+}, query, id2str.transform(), response('simcaqFirstReport'));
 
 module.exports = simcaqFirstReportApp;
diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
index ccdd2f9b..4e8e75f9 100644
--- a/src/libs/routes_v2/simcaqSecondReport.js
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -40,6 +40,8 @@ const cache = require('apicache').options({ debug: config.debug, statusCodes: {i
 
 let rqf = new ReqQueryFields();
 
+const id2str = require(`${libs}/middlewares/id2str`);
+
 simcaqSecondReportApp.use(cache('15 day'));
 
 rqf.addField({
@@ -125,6 +127,6 @@ simcaqSecondReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .group('ano_censo')
         .group('dependencia_adm_priv');
     next();
-}, query, response('simcaqSecondReport'));
+}, query, id2str.transform(), response('simcaqSecondReport'));
 
 module.exports = simcaqSecondReportApp;
-- 
GitLab


From ea6c6101497305d915be38f42c68adf573a7437d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 28 Mar 2023 11:56:53 -0300
Subject: [PATCH 074/123] Fix issue with id2str in simcaqSecondReport route

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

diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
index 4e8e75f9..cfe27ddd 100644
--- a/src/libs/routes_v2/simcaqSecondReport.js
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -122,7 +122,7 @@ simcaqSecondReportApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .field('sum(num_escolas_total)' ,'num_schools_total')
         .field('sum(num_escolas_rural)' ,'num_schools_rural')
         .field('sum(num_escolas_urbana)' ,'num_schools_urban')
-        .field('dependencia_adm_priv', 'private_adm_dependency')
+        .field('dependencia_adm_priv', 'adm_dependency_detailed_id')
         .field('ano_censo', 'year')
         .group('ano_censo')
         .group('dependencia_adm_priv');
-- 
GitLab


From e05e3e4af7ccf99640d2dbc8c016008ad36d9b8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 30 Mar 2023 09:17:51 -0300
Subject: [PATCH 075/123] Add division of responsibility route

---
 src/libs/convert/admDependencyPub.js          |  32 +++++
 src/libs/middlewares/id2str.js                |   5 +-
 src/libs/routes_v2/api.js                     |   3 +
 .../simcaqDivisionOfResponsibility.js         | 127 ++++++++++++++++++
 4 files changed, 166 insertions(+), 1 deletion(-)
 create mode 100644 src/libs/convert/admDependencyPub.js
 create mode 100644 src/libs/routes_v2/simcaqDivisionOfResponsibility.js

diff --git a/src/libs/convert/admDependencyPub.js b/src/libs/convert/admDependencyPub.js
new file mode 100644
index 00000000..0522b237
--- /dev/null
+++ b/src/libs/convert/admDependencyPub.js
@@ -0,0 +1,32 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+   
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+module.exports = function admDependencyPub(id) {
+    switch (id) {
+        case 1:
+        return 'Rede pública';
+        case 2:
+        return 'Rede estadual';
+        case 3:
+        return 'Rede municipal';
+        default:
+        return 'Não classificada';
+    }
+};
diff --git a/src/libs/middlewares/id2str.js b/src/libs/middlewares/id2str.js
index b59f6463..43cc1e0f 100644
--- a/src/libs/middlewares/id2str.js
+++ b/src/libs/middlewares/id2str.js
@@ -96,6 +96,7 @@ const diffLocation = require(`${libs}/convert/diffLocation`);
 const peePorCategoria = require(`${libs}/convert/peePorCategoria`);
 const pee = require(`${libs}/convert/booleanVariable`);
 const shift = require(`${libs}/convert/shift`);
+const admDependencyPub = require(`${libs}/convert/admDependencyPub`);
 
 const ids = {
     gender_id: gender,
@@ -107,6 +108,7 @@ const ids = {
     education_level_short_id: educationLevelShort,
     adm_dependency_id: admDependency,
     adm_dependency_detailed_id: admDependencyPriv,
+    adm_dependency_public_id: admDependencyPub,
     location_id: location,
     rural_location_id: ruralLocation,
     location_detailed_id: ruralLocation,
@@ -298,5 +300,6 @@ module.exports = {
     diffLocation,
     peePorCategoria,
     pee,
-    shift
+    shift,
+    admDependencyPub
 };
diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index f6804584..33822b82 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -140,6 +140,8 @@ const simcaqEnrollmentDiagnosis = require(`${libs}/routes_v2/simcaqEnrollmentDia
 
 const simcaqSecondReport = require(`${libs}/routes_v2/simcaqSecondReport`);
 
+const simcaqDivisionOfResponsibility = require(`${libs}/routes_v2/simcaqDivisionOfResponsibility`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -201,5 +203,6 @@ api.use('/course_students', courseStudents);
 api.use('/simcaq_first_report', simcaqFirstReport);
 api.use('/simcaq_enrollment_diagnosis', simcaqEnrollmentDiagnosis);
 api.use('/simcaq_second_report', simcaqSecondReport);
+api.use('/simcaq_division_of_responsibility', simcaqDivisionOfResponsibility);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqDivisionOfResponsibility.js b/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
new file mode 100644
index 00000000..e115ed41
--- /dev/null
+++ b/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
@@ -0,0 +1,127 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqDivisionOfResponsibilityApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqDivisionOfResponsibilityApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_divisao_de_responsabilidade'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_divisao_de_responsabilidade'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_divisao_de_responsabilidade'
+    }
+}, 'dims').addValue({
+    name: 'education_level_short_id',
+    table: 'simcaq_divisao_de_responsabilidade',
+    tableField: 'etapa',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa'
+    }
+}).addValue({
+    name: 'adm_dependency_public_id',
+    table: 'simcaq_divisao_de_responsabilidade',
+    tableField: 'rede',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'rede'
+    }
+});
+
+simcaqDivisionOfResponsibilityApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_divisao_de_responsabilidade')
+        .field('SUM(simcaq_divisao_de_responsabilidade.total_matriculas)', 'enrollments')
+        .field('simcaq_divisao_de_responsabilidade.etapa', 'education_level_short_id')
+        .field('simcaq_divisao_de_responsabilidade.rede', 'adm_dependency_public_id')
+        .group('simcaq_divisao_de_responsabilidade.etapa')
+        .group('simcaq_divisao_de_responsabilidade.rede');
+    next();
+}, query, id2str.transform(), response('divisionOfResponsibility'));
+
+module.exports = simcaqDivisionOfResponsibilityApp;
-- 
GitLab


From c310e021602b30c40c1a4fbfdba78014c6bf5661 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 3 Apr 2023 11:00:49 -0300
Subject: [PATCH 076/123] Add city_name, city_id, state_name, state_id fields
 to new routes

---
 src/libs/routes_v2/simcaqDivisionOfResponsibility.js | 6 ++++--
 src/libs/routes_v2/simcaqEnrollmentDiagnosis.js      | 6 ++++--
 src/libs/routes_v2/simcaqFirstReport.js              | 6 ++++--
 src/libs/routes_v2/simcaqSecondReport.js             | 6 ++++--
 4 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/src/libs/routes_v2/simcaqDivisionOfResponsibility.js b/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
index e115ed41..ca7b7c12 100644
--- a/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
+++ b/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
@@ -55,7 +55,8 @@ rqf.addField({
 }).addValue({
     name: 'city',
     table: 'municipio',
-    tableField: 'id',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
     where: {
         relation: '=',
         type: 'integer',
@@ -69,7 +70,8 @@ rqf.addField({
 }, 'dims').addValue({
     name: 'state',
     table: 'estado',
-    tableField: 'id',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
     where: {
         relation: '=',
         type: 'integer',
diff --git a/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
index d77aa191..1d6c7082 100644
--- a/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
+++ b/src/libs/routes_v2/simcaqEnrollmentDiagnosis.js
@@ -64,7 +64,8 @@ rqf.addField({
 }).addValue({
     name: 'city',
     table: 'municipio',
-    tableField: 'id',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
     where: {
         relation: '=',
         type: 'integer',
@@ -78,7 +79,8 @@ rqf.addField({
 }, 'dims').addValue({
     name: 'state',
     table: 'estado',
-    tableField: 'id',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
     where: {
         relation: '=',
         type: 'integer',
diff --git a/src/libs/routes_v2/simcaqFirstReport.js b/src/libs/routes_v2/simcaqFirstReport.js
index d8e3cf0f..6c037cf4 100644
--- a/src/libs/routes_v2/simcaqFirstReport.js
+++ b/src/libs/routes_v2/simcaqFirstReport.js
@@ -73,7 +73,8 @@ rqf.addField({
 }).addValue({
     name: 'city',
     table: 'municipio',
-    tableField: 'id',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
     where: {
         relation: '=',
         type: 'integer',
@@ -87,7 +88,8 @@ rqf.addField({
 }, 'dims').addValue({
     name: 'state',
     table: 'estado',
-    tableField: 'id',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
     where: {
         relation: '=',
         type: 'integer',
diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
index cfe27ddd..4c30f8d1 100644
--- a/src/libs/routes_v2/simcaqSecondReport.js
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -64,7 +64,8 @@ rqf.addField({
 }).addValue({
     name: 'city',
     table: 'municipio',
-    tableField: 'id',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
     where: {
         relation: '=',
         type: 'integer',
@@ -78,7 +79,8 @@ rqf.addField({
 }, 'dims').addValue({
     name: 'state',
     table: 'estado',
-    tableField: 'id',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
     where: {
         relation: '=',
         type: 'integer',
-- 
GitLab


From 28fb8ec67946673b55d66528fad3425e1f12d551 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 3 Apr 2023 11:44:20 -0300
Subject: [PATCH 077/123] Add year filter to the division of responsibility
 route

---
 src/libs/routes_v2/simcaqDivisionOfResponsibility.js | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/libs/routes_v2/simcaqDivisionOfResponsibility.js b/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
index ca7b7c12..3ec798e3 100644
--- a/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
+++ b/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
@@ -52,6 +52,15 @@ rqf.addField({
     name: 'dims',
     field: true,
     where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_divisao_de_responsabilidade',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
 }).addValue({
     name: 'city',
     table: 'municipio',
-- 
GitLab


From 84417f1e21a6acbdfd5974505420be04df740f35 Mon Sep 17 00:00:00 2001
From: ppc19 <ppc19@inf.ufpr.br>
Date: Tue, 4 Apr 2023 09:52:10 -0300
Subject: [PATCH 078/123] add year field to division of responsibility route

---
 src/libs/routes_v2/simcaqDivisionOfResponsibility.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libs/routes_v2/simcaqDivisionOfResponsibility.js b/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
index 3ec798e3..b9704b3b 100644
--- a/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
+++ b/src/libs/routes_v2/simcaqDivisionOfResponsibility.js
@@ -127,9 +127,11 @@ rqf.addField({
 
 simcaqDivisionOfResponsibilityApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.sql.from('simcaq_divisao_de_responsabilidade')
+        .field('simcaq_divisao_de_responsabilidade.ano_censo', 'year')
         .field('SUM(simcaq_divisao_de_responsabilidade.total_matriculas)', 'enrollments')
         .field('simcaq_divisao_de_responsabilidade.etapa', 'education_level_short_id')
         .field('simcaq_divisao_de_responsabilidade.rede', 'adm_dependency_public_id')
+        .group('simcaq_divisao_de_responsabilidade.ano_censo')
         .group('simcaq_divisao_de_responsabilidade.etapa')
         .group('simcaq_divisao_de_responsabilidade.rede');
     next();
-- 
GitLab


From c5362aa09fd121c80e9f3a142e0d8abe89d2cf51 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 4 Apr 2023 11:43:27 -0300
Subject: [PATCH 079/123] Add simcaq classroom size route

---
 src/libs/routes_v2/api.js                 |   3 +
 src/libs/routes_v2/simcaqClassroomSize.js | 147 ++++++++++++++++++++++
 2 files changed, 150 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqClassroomSize.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 33822b82..872274fe 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -142,6 +142,8 @@ const simcaqSecondReport = require(`${libs}/routes_v2/simcaqSecondReport`);
 
 const simcaqDivisionOfResponsibility = require(`${libs}/routes_v2/simcaqDivisionOfResponsibility`);
 
+const simcaqClassroomSize = require(`${libs}/routes_v2/simcaqClassroomSize`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -204,5 +206,6 @@ api.use('/simcaq_first_report', simcaqFirstReport);
 api.use('/simcaq_enrollment_diagnosis', simcaqEnrollmentDiagnosis);
 api.use('/simcaq_second_report', simcaqSecondReport);
 api.use('/simcaq_division_of_responsibility', simcaqDivisionOfResponsibility);
+api.use('/simcaq_classroom_size', simcaqClassroomSize);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqClassroomSize.js b/src/libs/routes_v2/simcaqClassroomSize.js
new file mode 100644
index 00000000..9b48f3b3
--- /dev/null
+++ b/src/libs/routes_v2/simcaqClassroomSize.js
@@ -0,0 +1,147 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqClassroomSizeApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqClassroomSizeApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_tamanho_das_turmas'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_tamanho_das_turmas'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_tamanho_das_turmas'
+    }
+}, 'dims').addValue({
+    name: 'education_level_short_id',
+    table: 'simcaq_tamanho_das_turmas',
+    tableField: 'etapa',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa'
+    }
+}).addValue({
+    name: 'location',
+    table: 'simcaq_tamanho_das_turmas',
+    tableField: 'localizacao_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'adm_dependency_public_id',
+    table: 'simcaq_tamanho_das_turmas',
+    tableField: 'rede',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'rede'
+    }
+}).addValue({
+    name: 'year',
+    table: 'simcaq_tamanho_das_turmas',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+});
+
+simcaqClassroomSizeApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_tamanho_das_turmas')
+        .field('AVG(simcaq_tamanho_das_turmas.num_matricula)', 'average_classroom_size')
+        .field('simcaq_tamanho_das_turmas.etapa', 'education_level_short_id')
+        .field('simcaq_tamanho_das_turmas.ano_censo', 'year')
+        .group('simcaq_tamanho_das_turmas.ano_censo')
+        .group('simcaq_tamanho_das_turmas.etapa');
+    next();
+}, query, id2str.transform(), response('classroomSize'));
+
+module.exports = simcaqClassroomSizeApp;
-- 
GitLab


From e8a005b4474582efcef0e1acdbd2858a608cd7a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 6 Apr 2023 10:03:58 -0300
Subject: [PATCH 080/123] Add workload and number of employees routes

---
 src/libs/routes_v2/api.js                     |   6 +
 src/libs/routes_v2/simcaqNumberOfEmployees.js | 129 +++++++++++++++
 src/libs/routes_v2/simcaqWorkload.js          | 151 ++++++++++++++++++
 3 files changed, 286 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqNumberOfEmployees.js
 create mode 100644 src/libs/routes_v2/simcaqWorkload.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 872274fe..fd345d05 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -144,6 +144,10 @@ const simcaqDivisionOfResponsibility = require(`${libs}/routes_v2/simcaqDivision
 
 const simcaqClassroomSize = require(`${libs}/routes_v2/simcaqClassroomSize`);
 
+const simcaqWorkload = require(`${libs}/routes_v2/simcaqWorkload`);
+
+const simcaqNumberOfEmployees = require(`${libs}/routes_v2/simcaqNumberOfEmployees`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -207,5 +211,7 @@ api.use('/simcaq_enrollment_diagnosis', simcaqEnrollmentDiagnosis);
 api.use('/simcaq_second_report', simcaqSecondReport);
 api.use('/simcaq_division_of_responsibility', simcaqDivisionOfResponsibility);
 api.use('/simcaq_classroom_size', simcaqClassroomSize);
+api.use('/simcaq_workload', simcaqWorkload);
+api.use('/simcaq_number_of_employees', simcaqNumberOfEmployees);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqNumberOfEmployees.js b/src/libs/routes_v2/simcaqNumberOfEmployees.js
new file mode 100644
index 00000000..58a28ffa
--- /dev/null
+++ b/src/libs/routes_v2/simcaqNumberOfEmployees.js
@@ -0,0 +1,129 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqNumberOfEmployeesApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqNumberOfEmployeesApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_numero_de_funcionarios',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_numero_de_funcionarios'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_numero_de_funcionarios'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_numero_de_funcionarios'
+    }
+}, 'dims').addValue({
+    name: 'adm_dependency_public_id',
+    table: 'simcaq_numero_de_funcionarios',
+    tableField: 'rede',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'rede'
+    }
+});
+
+simcaqNumberOfEmployeesApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_numero_de_funcionarios')
+        .field('SUM(num_funcionarios)', 'average_number_of_employees')
+        .field('simcaq_numero_de_funcionarios.rede', 'adm_dependency_public_id')
+        .field('simcaq_numero_de_funcionarios.ano_censo', 'year')
+        .group('simcaq_numero_de_funcionarios.rede')
+        .group('simcaq_numero_de_funcionarios.ano_censo');
+    next();
+}, query, id2str.transform(), response('simcaqNumberOfEmployees'));
+
+module.exports = simcaqNumberOfEmployeesApp;
diff --git a/src/libs/routes_v2/simcaqWorkload.js b/src/libs/routes_v2/simcaqWorkload.js
new file mode 100644
index 00000000..b4584224
--- /dev/null
+++ b/src/libs/routes_v2/simcaqWorkload.js
@@ -0,0 +1,151 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqWorkloadApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqWorkloadApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_carga_horaria_de_ensino',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_carga_horaria_de_ensino'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_carga_horaria_de_ensino'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_carga_horaria_de_ensino'
+    }
+}, 'dims').addValue({
+    name: 'adm_dependency_public_id',
+    table: 'simcaq_carga_horaria_de_ensino',
+    tableField: 'rede',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'rede'
+    }
+}).addValue({
+    name: 'education_level_short_id',
+    table: 'simcaq_carga_horaria_de_ensino',
+    tableField: 'etapa',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa'
+    }
+}).addValue({
+    name: 'shift_id',
+    table: 'simcaq_carga_horaria_de_ensino',
+    tableField: 'turno',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'turno'
+    }
+});
+
+simcaqWorkloadApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_carga_horaria_de_ensino')
+        .field('AVG(duracao_turma_horas)', 'average_workload')
+        .field('simcaq_carga_horaria_de_ensino.etapa', 'education_level_short_id')
+        .field('simcaq_carga_horaria_de_ensino.turno', 'shift_id')
+        .field('simcaq_carga_horaria_de_ensino.rede', 'adm_dependency_public_id')
+        .field('simcaq_carga_horaria_de_ensino.ano_censo', 'year')
+        .group('simcaq_carga_horaria_de_ensino.etapa')
+        .group('simcaq_carga_horaria_de_ensino.turno')
+        .group('simcaq_carga_horaria_de_ensino.rede')
+        .group('simcaq_carga_horaria_de_ensino.ano_censo');
+    next();
+}, query, id2str.transform(), response('simcaqWorkload'));
+
+module.exports = simcaqWorkloadApp;
-- 
GitLab


From fdaedcbecfbcdce89c9f237d5669908c287ab582 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 6 Apr 2023 11:05:59 -0300
Subject: [PATCH 081/123] Add new classes route

---
 src/libs/routes_v2/api.js                     |   3 +
 src/libs/routes_v2/simcaqNewClasses.js        | 131 ++++++++++++++++++
 src/libs/routes_v2/simcaqNumberOfEmployees.js |   2 +-
 3 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 src/libs/routes_v2/simcaqNewClasses.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index fd345d05..195a23e8 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -148,6 +148,8 @@ const simcaqWorkload = require(`${libs}/routes_v2/simcaqWorkload`);
 
 const simcaqNumberOfEmployees = require(`${libs}/routes_v2/simcaqNumberOfEmployees`);
 
+const simcaqNewClasses = require(`${libs}/routes_v2/simcaqNewClasses`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -213,5 +215,6 @@ api.use('/simcaq_division_of_responsibility', simcaqDivisionOfResponsibility);
 api.use('/simcaq_classroom_size', simcaqClassroomSize);
 api.use('/simcaq_workload', simcaqWorkload);
 api.use('/simcaq_number_of_employees', simcaqNumberOfEmployees);
+api.use('/simcaq_new_classes', simcaqNewClasses);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqNewClasses.js b/src/libs/routes_v2/simcaqNewClasses.js
new file mode 100644
index 00000000..4922270e
--- /dev/null
+++ b/src/libs/routes_v2/simcaqNewClasses.js
@@ -0,0 +1,131 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqNewClassesApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqNewClassesApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_novas_salas',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_novas_salas'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_novas_salas'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_novas_salas'
+    }
+}, 'dims').addValue({
+    name: 'adm_dependency_public_id',
+    table: 'simcaq_novas_salas',
+    tableField: 'rede',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'rede'
+    }
+});
+
+simcaqNewClassesApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_novas_salas')
+        .field('SUM(num_salas_urbana)', 'number_of_urban_classes')
+        .field('SUM(num_salas_rural)', 'number_of_rural_classes')
+        .field('SUM(num_salas_total)', 'total_number_of_classes')
+        .field('simcaq_novas_salas.rede', 'adm_dependency_public_id')
+        .field('simcaq_novas_salas.ano_censo', 'year')
+        .group('simcaq_novas_salas.rede')
+        .group('simcaq_novas_salas.ano_censo');
+    next();
+}, query, id2str.transform(), response('simcaqNewClasses'));
+
+module.exports = simcaqNewClassesApp;
diff --git a/src/libs/routes_v2/simcaqNumberOfEmployees.js b/src/libs/routes_v2/simcaqNumberOfEmployees.js
index 58a28ffa..3496aa09 100644
--- a/src/libs/routes_v2/simcaqNumberOfEmployees.js
+++ b/src/libs/routes_v2/simcaqNumberOfEmployees.js
@@ -118,7 +118,7 @@ rqf.addField({
 
 simcaqNumberOfEmployeesApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.sql.from('simcaq_numero_de_funcionarios')
-        .field('SUM(num_funcionarios)', 'average_number_of_employees')
+        .field('SUM(num_funcionarios)', 'number_of_employees')
         .field('simcaq_numero_de_funcionarios.rede', 'adm_dependency_public_id')
         .field('simcaq_numero_de_funcionarios.ano_censo', 'year')
         .group('simcaq_numero_de_funcionarios.rede')
-- 
GitLab


From 7ba4b926e690cec6ea590266c941da4197d1c5ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 10 Apr 2023 11:55:49 -0300
Subject: [PATCH 082/123] Add simcaq_result route, converter for
 supply_dimension

---
 src/libs/convert/supplyDimension.js       |  36 ++++++
 src/libs/middlewares/id2str.js            |   4 +-
 src/libs/routes_v2/api.js                 |   3 +
 src/libs/routes_v2/simcaqClassroomSize.js |   2 +-
 src/libs/routes_v2/simcaqResult.js        | 140 ++++++++++++++++++++++
 5 files changed, 183 insertions(+), 2 deletions(-)
 create mode 100644 src/libs/convert/supplyDimension.js
 create mode 100644 src/libs/routes_v2/simcaqResult.js

diff --git a/src/libs/convert/supplyDimension.js b/src/libs/convert/supplyDimension.js
new file mode 100644
index 00000000..40f5ca9a
--- /dev/null
+++ b/src/libs/convert/supplyDimension.js
@@ -0,0 +1,36 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+module.exports = function supplyDimension(id) {
+    switch(id) {
+        case 1:
+            return 'Número de matrículas';
+        case 2:
+            return 'Número de turmas';
+        case 3:
+            return 'Número de salas';
+        case 4:
+            return 'Número de professores';
+        case 5:
+            return 'Número de funcionários';
+        default:
+            return 'Não especificado';
+    }
+}
\ No newline at end of file
diff --git a/src/libs/middlewares/id2str.js b/src/libs/middlewares/id2str.js
index 43cc1e0f..8c2b068d 100644
--- a/src/libs/middlewares/id2str.js
+++ b/src/libs/middlewares/id2str.js
@@ -97,6 +97,7 @@ const peePorCategoria = require(`${libs}/convert/peePorCategoria`);
 const pee = require(`${libs}/convert/booleanVariable`);
 const shift = require(`${libs}/convert/shift`);
 const admDependencyPub = require(`${libs}/convert/admDependencyPub`);
+const supplyDimension = require(`${libs}/convert/supplyDimension`);
 
 const ids = {
     gender_id: gender,
@@ -185,7 +186,8 @@ const ids = {
     diff_location_id: diffLocation,
     pee_por_categoria: peePorCategoria,
     pee_id: pee,
-    shift_id: shift
+    shift_id: shift,
+    supply_dimension_id: supplyDimension
 };
 
 function transform(removeId=false) {
diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 195a23e8..b675c882 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -150,6 +150,8 @@ const simcaqNumberOfEmployees = require(`${libs}/routes_v2/simcaqNumberOfEmploye
 
 const simcaqNewClasses = require(`${libs}/routes_v2/simcaqNewClasses`);
 
+const simcaqResult = require(`${libs}/routes_v2/simcaqResult`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -216,5 +218,6 @@ api.use('/simcaq_classroom_size', simcaqClassroomSize);
 api.use('/simcaq_workload', simcaqWorkload);
 api.use('/simcaq_number_of_employees', simcaqNumberOfEmployees);
 api.use('/simcaq_new_classes', simcaqNewClasses);
+api.use('/simcaq_result', simcaqResult);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqClassroomSize.js b/src/libs/routes_v2/simcaqClassroomSize.js
index 9b48f3b3..d70d084e 100644
--- a/src/libs/routes_v2/simcaqClassroomSize.js
+++ b/src/libs/routes_v2/simcaqClassroomSize.js
@@ -142,6 +142,6 @@ simcaqClassroomSizeApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
         .group('simcaq_tamanho_das_turmas.ano_censo')
         .group('simcaq_tamanho_das_turmas.etapa');
     next();
-}, query, id2str.transform(), response('classroomSize'));
+}, query, id2str.transform(), response('simcaqClassroomSize'));
 
 module.exports = simcaqClassroomSizeApp;
diff --git a/src/libs/routes_v2/simcaqResult.js b/src/libs/routes_v2/simcaqResult.js
new file mode 100644
index 00000000..3ba50ad0
--- /dev/null
+++ b/src/libs/routes_v2/simcaqResult.js
@@ -0,0 +1,140 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqResultApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqResultApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_resultado'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_resultado'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_resultado'
+    }
+}, 'dims').addValue({
+    name: 'adm_dependency_public_id',
+    table: 'simcaq_resultado',
+    tableField: 'rede',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'rede'
+    }
+}).addValue({
+    name: 'year',
+    table: 'simcaq_resultado',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'supply_dimension_id',
+    table: 'simcaq_resultado',
+    tableField: 'dimensao_oferta',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dimensao_oferta'
+    }
+});
+
+simcaqResultApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_resultado')
+        .field('SUM(simcaq_resultado.atual)', 'current')
+        .field('simcaq_resultado.rede', 'adm_dependency_public_id')
+        .field('simcaq_resultado.ano_censo', 'year')
+        .field('simcaq_resultado.dimensao_oferta', 'supply_dimension_id')
+        .group('simcaq_resultado.ano_censo')
+        .group('simcaq_resultado.rede')
+        .group('simcaq_resultado.dimensao_oferta');
+    next();
+}, query, id2str.transform(), response('simcaqResult'));
+
+module.exports = simcaqResultApp;
-- 
GitLab


From fc6b6bfb7946af09ac1501a0a2cf361ab2a205e8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 11 Apr 2023 11:23:56 -0300
Subject: [PATCH 083/123] Add number of teachers route

---
 src/libs/convert/level.js                    |  32 ++++++
 src/libs/convert/type.js                     |  30 ++++++
 src/libs/middlewares/id2str.js               |   6 +-
 src/libs/routes_v2/api.js                    |   3 +
 src/libs/routes_v2/simcaqNumberOfTeachers.js | 103 +++++++++++++++++++
 5 files changed, 173 insertions(+), 1 deletion(-)
 create mode 100644 src/libs/convert/level.js
 create mode 100644 src/libs/convert/type.js
 create mode 100644 src/libs/routes_v2/simcaqNumberOfTeachers.js

diff --git a/src/libs/convert/level.js b/src/libs/convert/level.js
new file mode 100644
index 00000000..e52a331b
--- /dev/null
+++ b/src/libs/convert/level.js
@@ -0,0 +1,32 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+module.exports = function level(id) {
+    switch(id) {
+        case 1:
+            return 'Brasil';
+        case 2:
+            return 'Estadual';
+        case 3:
+            return 'Municipal';
+        default:
+            return 'Não especificado';
+    }
+}
\ No newline at end of file
diff --git a/src/libs/convert/type.js b/src/libs/convert/type.js
new file mode 100644
index 00000000..6ffaf010
--- /dev/null
+++ b/src/libs/convert/type.js
@@ -0,0 +1,30 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+module.exports = function type(id) {
+    switch(id) {
+        case 1:
+            return 'Plano';
+        case 2:
+            return 'Rede';
+        default:
+            return 'Não especificado';
+    }
+}
\ No newline at end of file
diff --git a/src/libs/middlewares/id2str.js b/src/libs/middlewares/id2str.js
index 8c2b068d..39d819aa 100644
--- a/src/libs/middlewares/id2str.js
+++ b/src/libs/middlewares/id2str.js
@@ -98,6 +98,8 @@ const pee = require(`${libs}/convert/booleanVariable`);
 const shift = require(`${libs}/convert/shift`);
 const admDependencyPub = require(`${libs}/convert/admDependencyPub`);
 const supplyDimension = require(`${libs}/convert/supplyDimension`);
+const type = require(`${libs}/convert/type`);
+const level = require(`${libs}/convert/level`);
 
 const ids = {
     gender_id: gender,
@@ -187,7 +189,9 @@ const ids = {
     pee_por_categoria: peePorCategoria,
     pee_id: pee,
     shift_id: shift,
-    supply_dimension_id: supplyDimension
+    supply_dimension_id: supplyDimension,
+    type_id: type,
+    level_id: level
 };
 
 function transform(removeId=false) {
diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index b675c882..f63dd586 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -152,6 +152,8 @@ const simcaqNewClasses = require(`${libs}/routes_v2/simcaqNewClasses`);
 
 const simcaqResult = require(`${libs}/routes_v2/simcaqResult`);
 
+const simcaqNumberOfTeachers = require(`${libs}/routes_v2/simcaqNumberOfTeachers`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -219,5 +221,6 @@ api.use('/simcaq_workload', simcaqWorkload);
 api.use('/simcaq_number_of_employees', simcaqNumberOfEmployees);
 api.use('/simcaq_new_classes', simcaqNewClasses);
 api.use('/simcaq_result', simcaqResult);
+api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqNumberOfTeachers.js b/src/libs/routes_v2/simcaqNumberOfTeachers.js
new file mode 100644
index 00000000..00f6d942
--- /dev/null
+++ b/src/libs/routes_v2/simcaqNumberOfTeachers.js
@@ -0,0 +1,103 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqNumberOfTeachersApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqNumberOfTeachersApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_num_professores',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'local_id',
+    table: 'simcaq_num_professores',
+    tableField: 'local_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'local_id'
+    }
+}).addValue({
+    name: 'level_id',
+    table: 'simcaq_num_professores',
+    tableField: 'nivel',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'nivel'
+    }
+}).addValue({
+    name: 'type_id',
+    table: 'simcaq_num_professores',
+    tableField: 'tipo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'tipo'
+    }
+});
+
+simcaqNumberOfTeachersApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_num_professores')
+        .field('total', 'number_of_teachers')
+        .field('simcaq_num_professores.ano_censo', 'year')
+        .field('simcaq_num_professores.nivel', 'level_id')
+        .field('simcaq_num_professores.tipo', 'type_id')
+        .field('simcaq_num_professores.local_id', 'local_id');
+    next();
+}, query, id2str.transform(), response('simcaqNumberOfTeachers'));
+
+module.exports = simcaqNumberOfTeachersApp;
-- 
GitLab


From 5d14877ab75a025f2b334cdf85923b44cc41f116 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Wed, 12 Apr 2023 11:17:32 -0300
Subject: [PATCH 084/123] fix issue with the simcaq_second_report_route where
 the adm_dependency_detailed_id filter wasn't correctly set up

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

diff --git a/src/libs/routes_v2/simcaqSecondReport.js b/src/libs/routes_v2/simcaqSecondReport.js
index 4c30f8d1..421bd6f2 100644
--- a/src/libs/routes_v2/simcaqSecondReport.js
+++ b/src/libs/routes_v2/simcaqSecondReport.js
@@ -106,13 +106,13 @@ rqf.addField({
         foreignTable: 'simcaq_relatorio_2'
     }
 }, 'dims').addValue({
-    name: 'adm_dependency',
+    name: 'adm_dependency_detailed_id',
     table: 'simcaq_relatorio_2',
-    tableField: 'dependencia_adm_id',
+    tableField: 'dependencia_adm_priv',
     where: {
         relation: '=',
         type: 'integer',
-        field: 'dependencia_adm_id'
+        field: 'dependencia_adm_priv'
     }
 });
 
-- 
GitLab


From fb2573bc2a4611876f515070ed5c1dde56987259 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 18 Apr 2023 10:04:09 -0300
Subject: [PATCH 085/123] Add enrollment projection route

---
 src/libs/routes_v2/api.js                     |   3 +
 .../routes_v2/simcaqEnrollmentProjection.js   | 143 ++++++++++++++++++
 2 files changed, 146 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqEnrollmentProjection.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index f63dd586..25c829f9 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -154,6 +154,8 @@ const simcaqResult = require(`${libs}/routes_v2/simcaqResult`);
 
 const simcaqNumberOfTeachers = require(`${libs}/routes_v2/simcaqNumberOfTeachers`);
 
+const simcaqEnrollmentProjection = require(`${libs}/routes_v2/simcaqEnrollmentProjection`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -222,5 +224,6 @@ api.use('/simcaq_number_of_employees', simcaqNumberOfEmployees);
 api.use('/simcaq_new_classes', simcaqNewClasses);
 api.use('/simcaq_result', simcaqResult);
 api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
+api.use('/simcaq_enrollment_projection', simcaqEnrollmentProjection);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqEnrollmentProjection.js b/src/libs/routes_v2/simcaqEnrollmentProjection.js
new file mode 100644
index 00000000..cb472b4e
--- /dev/null
+++ b/src/libs/routes_v2/simcaqEnrollmentProjection.js
@@ -0,0 +1,143 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqEnrollmentProjectionApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqEnrollmentProjectionApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_projecao_de_matricula',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_projecao_de_matricula'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_projecao_de_matricula'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_projecao_de_matricula'
+    }
+}, 'dims').addValue({
+    name: 'education_level_short_id',
+    table: 'simcaq_projecao_de_matricula',
+    tableField: 'etapa',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa'
+    }
+}).addValue({
+    name: 'adm_dependency_public_id',
+    table: 'simcaq_projecao_de_matricula',
+    tableField: 'rede',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'rede'
+    }
+});
+
+simcaqEnrollmentProjectionApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_projecao_de_matricula')
+        .field('simcaq_projecao_de_matricula.ano_censo', 'year')
+        .field('simcaq_projecao_de_matricula.etapa', 'education_level_short_id')
+        .field('simcaq_projecao_de_matricula.rede', 'adm_dependency_public_id')
+        .field('SUM(simcaq_projecao_de_matricula.total_matriculas)', 'total_enrollments')
+        .field('SUM(simcaq_projecao_de_matricula.matriculas_diurno)', 'daytime_enrollments')
+        .field('SUM(simcaq_projecao_de_matricula.matriculas_noturno)', 'nighttime_enrollments')
+        .field('SUM(simcaq_projecao_de_matricula.total_tempo_integral)', 'fulltime_enrollments')
+        .group('simcaq_projecao_de_matricula.ano_censo')
+        .group('simcaq_projecao_de_matricula.etapa')
+        .group('simcaq_projecao_de_matricula.rede');
+    next();
+}, query, id2str.transform(), response('enrollmentProjection'));
+
+module.exports = simcaqEnrollmentProjectionApp;
-- 
GitLab


From 2f3281ac1047aef6339d91089e50dac84e095eef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 24 Apr 2023 10:02:33 -0300
Subject: [PATCH 086/123] update v2 classroom route to use new aggregated table

---
 src/libs/routes_v2/classroom.js | 54 ++++++++++++++++-----------------
 1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/libs/routes_v2/classroom.js b/src/libs/routes_v2/classroom.js
index 8e2b6a7e..a3d6ec7f 100644
--- a/src/libs/routes_v2/classroom.js
+++ b/src/libs/routes_v2/classroom.js
@@ -50,22 +50,22 @@ let rqfCount = new ReqQueryFields();
 // Complete range of the enrollments dataset.
 // Returns a tuple of start and ending years of the complete enrollments dataset.
 classroomApp.get('/year_range', (req, res, next) => {
-    req.sql.from('escola')
-    .field('MIN(escola.ano_censo)', 'start_year')
-    .field('MAX(escola.ano_censo)', 'end_year');
+    req.sql.from('escola_agregada')
+    .field('MIN(escola_agregada.ano_censo)', 'start_year')
+    .field('MAX(escola_agregada.ano_censo)', 'end_year');
     next();
 }, query, response('range'));
 
 classroomApp.get('/years', (req, res, next) => {
-    req.sql.from('escola')
-    .field('DISTINCT escola.ano_censo', 'year');
+    req.sql.from('escola_agregada')
+    .field('DISTINCT escola_agregada.ano_censo', 'year');
     next();
 }, query, response('years'));
 
 classroomApp.get('/source', (req, res, next) => {
     req.sql.from('fonte')
     .field('fonte', 'source')
-    .where('tabela = \'escola\'');
+    .where('tabela = \'escola_agregada\'');
     next();
 }, query, response('source'));
 
@@ -109,7 +109,7 @@ rqf.addField({
     where: false
 }).addValueToField({
     name: 'school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: ['nome_escola', 'id'],
     resultField: ['school_name', 'school_id'],
     where: {
@@ -119,7 +119,7 @@ rqf.addField({
     }
 }, 'dims').addValueToField({
     name: 'school',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'nome_escola',
     resultField: 'school_name',
     where: {
@@ -136,12 +136,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'dims').addValueToField({
     name: 'city',
@@ -152,12 +152,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'municipio_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }, 'filter').addValue({
     name: 'state',
@@ -168,12 +168,12 @@ rqf.addField({
         relation: '=',
         type: 'integer',
         field: 'estado_id',
-        table: 'escola'
+        table: 'escola_agregada'
     },
     join: {
         primary: 'id',
         foreign: 'estado_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'region',
@@ -188,11 +188,11 @@ rqf.addField({
     join: {
         primary: 'id',
         foreign: 'regiao_id',
-        foreignTable: 'escola'
+        foreignTable: 'escola_agregada'
     }
 }).addValue({
     name: 'min_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -202,7 +202,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'max_year',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'ano_censo',
     resultField: 'year',
     where: {
@@ -212,7 +212,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'adm_dependency',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_id',
     resultField: 'adm_dependency_id',
     where: {
@@ -222,7 +222,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'adm_dependency_detailed',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'dependencia_adm_priv',
     resultField: 'adm_dependency_detailed_id',
     where: {
@@ -232,7 +232,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'location',
-    table: 'escola',
+    table: 'escola_agregada',
     tableField: 'localizacao_id',
     resultField: 'location_id',
     where: {
@@ -243,14 +243,14 @@ rqf.addField({
 });
 
 classroomApp.get('/', cache('15 day'), rqf.parse(), rqf.build(), (req, res, next) => {
-    req.sql.from('escola')
-        .field('SUM(escola.qtde_salas_utilizadas_dentro)', 'total')
+    req.sql.from('escola_agregada')
+        .field('SUM(escola_agregada.qtde_salas_utilizadas_dentro)', 'total')
         .field("'Brasil'", 'name')
-        .field('escola.ano_censo', 'year')
-        .group('escola.ano_censo')
-        .order('escola.ano_censo')
-        .where('escola.situacao_de_funcionamento = 1 AND escola.local_func_predio_escolar = 1')
-        .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1');
+        .field('escola_agregada.ano_censo', 'year')
+        .group('escola_agregada.ano_censo')
+        .order('escola_agregada.ano_censo')
+        .where('escola_agregada.situacao_funcionamento_pareada = 1 AND escola_agregada.local_func_predio_escolar = 1')
+        .where('escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1');
     next();
 }, query, addMissing(rqf), id2str.transform(), (req, res, next) => {
     if (req.dims.location && req.result.length < 2) {    // Garantimos que conterá as duas localizações no resultado para o simCAQ
-- 
GitLab


From d00a8d95f454b1b56477b6c79ea632f786a75c45 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 27 Apr 2023 11:02:11 -0300
Subject: [PATCH 087/123] Add simcaq aggregated enrollment

---
 src/libs/routes_v2/api.js                     |   3 +
 .../routes_v2/simcaqAggregatedEnrollment.js   | 211 ++++++++++++++++++
 2 files changed, 214 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqAggregatedEnrollment.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 25c829f9..2072e864 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -156,6 +156,8 @@ const simcaqNumberOfTeachers = require(`${libs}/routes_v2/simcaqNumberOfTeachers
 
 const simcaqEnrollmentProjection = require(`${libs}/routes_v2/simcaqEnrollmentProjection`);
 
+const simcaqAggregatedEnrollment = require(`${libs}/routes_v2/simcaqAggregatedEnrollment`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -225,5 +227,6 @@ api.use('/simcaq_new_classes', simcaqNewClasses);
 api.use('/simcaq_result', simcaqResult);
 api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
 api.use('/simcaq_enrollment_projection', simcaqEnrollmentProjection);
+api.use('/simcaq_aggregated_enrollment', simcaqAggregatedEnrollment);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqAggregatedEnrollment.js b/src/libs/routes_v2/simcaqAggregatedEnrollment.js
new file mode 100644
index 00000000..7562acfa
--- /dev/null
+++ b/src/libs/routes_v2/simcaqAggregatedEnrollment.js
@@ -0,0 +1,211 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqAggregatedEnrollmentApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqAggregatedEnrollmentApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'municipio_id',
+        foreignTable: 'simcaq_matricula_agregada'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'estado_id',
+        foreignTable: 'simcaq_matricula_agregada'
+    }
+}, 'dims').addValue({
+    name: 'region',
+    table: 'regiao',
+    tableField: ['nome', 'id'],
+    resultField: ['region_name', 'region_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'regiao_id',
+        foreignTable: 'simcaq_matricula_agregada'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: 'id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_matricula_agregada'
+    }
+}, 'dims').addValue({
+    name: 'locale',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'localizacao_id',
+    resultField: 'locale_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'adm_dependency_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+}).addValue({
+    name: 'adm_dependency_detailed_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'dependencia_adm_priv',
+    resultField: 'adm_dependency_detailed_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_priv'
+    }
+}).addValue({
+    name: 'diff_location_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'localizacao_diferenciada_par',
+    resultField: 'diff_location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_diferenciada_par'
+    }
+}).addValue({
+    name: 'school_building',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'local_func_predio_escolar',
+    where: {
+        relation: '=',
+        type: 'boolean',
+        field: 'local_func_predio_escolar'
+    }
+}).addValue({
+    name: 'rural_location_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'localidade_area_rural',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localidade_area_rural'
+    }
+}).addValue({
+    name: 'school_year_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'serie_ano_id',
+    resultField: 'school_year_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'serie_ano_id'
+    }
+}).addValue({
+    name: 'education_level_short_id',
+    table: 'simcaq_matricula_agregada',
+    tableField: 'etapa_resumida',
+    resultField: 'education_level_short_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'etapa_resumida'
+    }
+});
+
+simcaqAggregatedEnrollmentApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_matricula_agregada')
+        .field('SUM(simcaq_matricula_agregada.num_matriculas)', 'num_enrollments')
+        .field('simcaq_matricula_agregada.ano_censo', 'year')
+        .group('simcaq_matricula_agregada.ano_censo');
+    next();
+}, query, id2str.transform(), response('aggregatedEnrollment'));
+
+module.exports = simcaqAggregatedEnrollmentApp;
-- 
GitLab


From 18acaae498d13f19ed36ba4c0c18020c90ca024d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 28 Apr 2023 09:55:08 -0300
Subject: [PATCH 088/123] Add teacher city plan route

---
 src/libs/routes_v2/api.js                   |   3 +
 src/libs/routes_v2/simcaqTeacherCityPlan.js | 139 ++++++++++++++++++++
 2 files changed, 142 insertions(+)
 create mode 100644 src/libs/routes_v2/simcaqTeacherCityPlan.js

diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 2072e864..5c00d3fe 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -158,6 +158,8 @@ const simcaqEnrollmentProjection = require(`${libs}/routes_v2/simcaqEnrollmentPr
 
 const simcaqAggregatedEnrollment = require(`${libs}/routes_v2/simcaqAggregatedEnrollment`);
 
+const simcaqTeacherCityPlan = require(`${libs}/routes_v2/simcaqTeacherCityPlan`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -228,5 +230,6 @@ api.use('/simcaq_result', simcaqResult);
 api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
 api.use('/simcaq_enrollment_projection', simcaqEnrollmentProjection);
 api.use('/simcaq_aggregated_enrollment', simcaqAggregatedEnrollment);
+api.use('/simcaq_teacher_city_plan', simcaqTeacherCityPlan);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqTeacherCityPlan.js b/src/libs/routes_v2/simcaqTeacherCityPlan.js
new file mode 100644
index 00000000..72c19913
--- /dev/null
+++ b/src/libs/routes_v2/simcaqTeacherCityPlan.js
@@ -0,0 +1,139 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqTeacherCityPlanApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqTeacherCityPlanApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_docente_municipio_plano',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'city',
+    table: 'municipio',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_municipio_id',
+        foreignTable: 'simcaq_docente_municipio_plano'
+    }
+}, 'dims').addValue({
+    name: 'state',
+    table: 'estado',
+    tableField: ['nome', 'id'],
+    resultField: ['state_name', 'state_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_estado_id',
+        foreignTable: 'simcaq_docente_municipio_plano'
+    }
+}, 'dims').addValue({
+    name: 'school',
+    table: 'escola_agregada',
+    tableField: ['nome', 'id'],
+    resultField: ['school_name', 'school_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'id'
+    },
+    join: {
+        primary: 'id',
+        foreign: 'escola_id',
+        foreignTable: 'simcaq_docente_municipio_plano'
+    }
+}, 'dims').addValue({
+    name: 'country',
+    table: 'simcaq_docente_municipio_plano',
+    tableField: ['escola_pais_nome', 'escola_pais_id'],
+    resultField: ['country_name', 'country_id'],
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'escola_pais_id'
+    }
+}).addValue({
+    name: 'adm_dependency_id',
+    table: 'simcaq_docente_municipio_plano',
+    tableField: 'dependencia_adm_id',
+    resultField: 'adm_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'dependencia_adm_id'
+    }
+});
+
+simcaqTeacherCityPlanApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_docente_municipio_plano')
+        .field('simcaq_docente_municipio_plano.num_docentes', 'num_teachers')
+        .field('simcaq_docente_municipio_plano.dependencia_adm_id', 'adm_dependency_id')
+        .field('simcaq_docente_municipio_plano.ano_censo', 'year');
+    next();
+}, query, id2str.transform(), response('simcaqTeacherCityPlan'));
+
+module.exports = simcaqTeacherCityPlanApp;
-- 
GitLab


From 5b43bf569e5bcc47a01c2319e4a93dbf9a14a90e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Wed, 24 May 2023 11:08:00 -0300
Subject: [PATCH 089/123] Initiate changes to aggregated classroom_count, not
 ready yet

---
 src/libs/middlewares/query.js        |   2 +
 src/libs/routes_v2/aux               |   1 +
 src/libs/routes_v2/classroomCount.js | 149 +++++++++++----------------
 3 files changed, 63 insertions(+), 89 deletions(-)
 create mode 100644 src/libs/routes_v2/aux

diff --git a/src/libs/middlewares/query.js b/src/libs/middlewares/query.js
index b724d93d..3b734957 100644
--- a/src/libs/middlewares/query.js
+++ b/src/libs/middlewares/query.js
@@ -10,6 +10,8 @@ function query(req, res, next) {
     execute(sql.text, sql.values, (err, result) => {
         if(err) {
             log.error(err.stack);
+            console.log(sql.text);
+            console.log(sql.values);
             next(new Error('Request could not be satisfied due to a database error.'));
         } else {
             req.result = result;
diff --git a/src/libs/routes_v2/aux b/src/libs/routes_v2/aux
new file mode 100644
index 00000000..c0b4cc5f
--- /dev/null
+++ b/src/libs/routes_v2/aux
@@ -0,0 +1 @@
+SELECT COUNT(*) AS "total", simcaq_matricula_agregada.ano_censo AS "year", estado.nome AS "state_name", estado.id AS "state_id", municipio.nome AS "city_name", municipio.id AS "city_id", simcaq_matricula_agregada.etapa_resumida AS "education_level_short_id", simcaq_matricula_agregada.tempo_integral AS "integral_time_id" FROM simcaq_matricula_agregada INNER JOIN estado ON (simcaq_matricula_agregada.estado_id=estado.id) INNER JOIN municipio ON (simcaq_matricula_agregada.municipio_id=municipio.id) WHERE (simcaq_matricula_agregada.ano_censo >= 2021 ) AND (simcaq_matricula_agregada.ano_censo <= 2021 ) GROUP BY simcaq_matricula_agregada.ano_censo, estado.nome, estado.id, municipio.nome, municipio.id, simcaq_matricula_agregada.etapa_resumida, simcaq_matricula_agregada.tempo_integral ORDER BY simcaq_matricula_agregada.ano_censo ASC, estado.nome ASC, estado.id ASC, municipio.nome ASC, municipio.id ASC, simcaq_matricula_agregada.etapa_resumida ASC, simcaq_matricula_agregada.tempo_integral ASC
diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 0af32828..b4ba338a 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -54,21 +54,20 @@ rqf.addField({
 }).addValueToField({
     name: 'city',
     table: 'municipio',
-    tableField: 'nome',
-    resultField: 'city_name',
+    tableField: ['nome', 'id'],
+    resultField: ['city_name', 'city_id'],
     where: {
         relation: '=',
         type: 'integer',
-        field: 'municipio_id',
-        table: '@'
+        field: 'id'
     },
     join: {
         primary: 'id',
         foreign: 'municipio_id',
         foreignTable: '@'
     }
-}, 'filter').addValueToField({
-    name: 'city',
+}, 'dims').addValueToField({
+    name: 'cityTeacher',
     table: 'municipio',
     tableField: ['nome', 'id'],
     resultField: ['city_name', 'city_id'],
@@ -79,26 +78,10 @@ rqf.addField({
     },
     join: {
         primary: 'id',
-        foreign: 'municipio_id',
+        foreign: 'escola_cidade_id',
         foreignTable: '@'
     }
 }, 'dims').addValueToField({
-    name: 'state',
-    table: 'estado',
-    tableField: 'nome',
-    resultField: 'state_name',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'estado_id',
-        table: '@'
-    },
-    join: {
-        primary: 'id',
-        foreign: 'estado_id',
-        foreignTable: '@'
-    }
-}, 'filter').addValueToField({
     name: 'state',
     table: 'estado',
     tableField: ['nome', 'id'],
@@ -115,9 +98,9 @@ rqf.addField({
     }
 }, 'dims').addValueToField({
     name: 'school',
-    table: 'escola',
-    tableField: ['nome_escola', 'id', 'dependencia_adm_id'], // Dado de dependencia administrativa sempre deve ser retornado com escola
-    resultField: ['school_name', 'school_id', 'adm_dependency_id'],
+    table: 'escola_agregada',
+    tableField: ['nome_escola', 'id'],
+    resultField: ['school_name', 'school_id'],
     where: {
         relation: '=',
         type: 'integer',
@@ -163,39 +146,9 @@ rqf.addField({
         type: 'integer',
         field: 'ano_censo'
     }
-}).addValue({
-    name: 'school_year',
-    table: '@',
-    tableField: 'serie_ano_id',
-    resultField: 'school_year_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'serie_ano_id'
-    }
-}).addValue({
-    name: 'location',
-    table: '@',
-    tableField: 'localizacao_id',
-    resultField: 'location_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'localizacao_id'
-    }
-}).addValue({
-    name: 'period',
-    table: '@',
-    tableField: 'turma_turno_id',
-    resultField: 'period_id',
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'turma_turno_id'
-    }
 }).addValue({
     name: 'school_building',
-    table: 'escola',
+    table: '@',
     tableField: 'local_func_predio_escolar',
     resultField: 'school_building',
     where: {
@@ -205,7 +158,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'night_time',
-    table: 'matricula',
+    table: '@',
     tableField: 'periodo_noturno',
     resultField: 'night_time',
     where: {
@@ -215,7 +168,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'formation_level',
-    table: 'docente_por_formacao',
+    table: '@',
     tableField: 'tipo_formacao',
     resultField: 'formation_level',
     where: {
@@ -235,7 +188,7 @@ rqf.addField({
     }
 }, 'filter') .addValue({
     name: 'integral_time',
-    table: 'matricula',
+    table: '@',
     tableField: 'tempo_integral',
     resultField: 'integral_time_id',
     where: {
@@ -245,7 +198,7 @@ rqf.addField({
     }
 }).addValue({
     name: 'education_level_short',
-    table: 'matricula',
+    table: '@',
     tableField: 'etapa_resumida',
     resultField: 'education_level_short_id',
     where: {
@@ -253,6 +206,26 @@ rqf.addField({
         type: 'integer',
         field: 'etapa_resumida'
     }
+}).addValue({
+    name: 'location',
+    table: '@',
+    tableField: 'localizacao_id',
+    resultField: 'location_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'localizacao_id'
+    }
+}).addValue({
+    name: 'school_year',
+    table: '@',
+    tableField: 'ano_censo',
+    resultField: 'school_year',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
 });
 
 classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
@@ -276,11 +249,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.sql.field('sum(dia_total)', 'total_day')
     .field('sum(noite_total)', 'total_night')
     .field("'Brasil'", 'name')
-    .field('matricula_por_localizacao.ano_censo', 'year')
-    .from('matricula_por_localizacao')
-    .where('matricula_por_localizacao.serie_ano_id < 15')
-    .group('matricula_por_localizacao.ano_censo')
-    .order('matricula_por_localizacao.ano_censo')
+    .field('simcaq_matricula_por_localizacao.ano_censo', 'year')
+    .from('simcaq_matricula_por_localizacao')
+    .where('simcaq_matricula_por_localizacao.serie_ano_id < 15')
+    .group('simcaq_matricula_por_localizacao.ano_censo')
+    .order('simcaq_matricula_por_localizacao.ano_censo')
 
     next();
 }, rqf.build(), query, id2str.transform(), (req, res, next) => {
@@ -310,7 +283,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             };
         }
     }
-    
+
     delete req.dims;
     delete req.filter;
     req.resetSql();
@@ -322,15 +295,15 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.dims.location = true;
     req.dims.school_building = true;
 
-    req.sql.field('SUM(escola.qtde_salas_utilizadas_dentro)', 'total')
+    req.sql.field('SUM(escola_agregada.qtde_salas_utilizadas_dentro)', 'total')
     .field("'Brasil'", 'name')
-    .field('escola.ano_censo', 'year')
-    .from('escola')
-    .group('escola.ano_censo')
-    .order('escola.ano_censo')
-    .where('escola.situacao_de_funcionamento = 1')
-    .where('escola.dependencia_adm_id < 4') 
-    .where('escola.ensino_regular = 1 OR escola.ensino_eja = 1 OR escola.educacao_profissional = 1');
+    .field('escola_agregada.ano_censo', 'year')
+    .from('escola_agregada')
+    .group('escola_agregada.ano_censo')
+    .order('escola_agregada.ano_censo')
+    .where('escola_agregada.situacao_funcionamento_pareada = 1')
+    .where('escola_agregada.dependencia_adm_id < 4') 
+    .where('escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1');
 
     next();
 }, rqf.build(), query, id2str.transform(), (req, res, next) =>  {
@@ -379,15 +352,14 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         req.teacherCalc = true;
     }
 
-    req.dims.state = true;
-    req.dims.city = true;
+    req.dims.cityTeacher = true;
     req.dims.formation_level = true;
-    req.sql.field('count(distinct docente_por_formacao.id_docente)', 'total')
+    req.sql.field('SUM(simcaq_docente_agregada.num_docentes)', 'total')
            .field("'Brasil'", 'name')
-           .field('docente_por_formacao.ano_censo', 'year')
-           .from('docente_por_formacao')
-           .group('docente_por_formacao.ano_censo')
-           .order('docente_por_formacao.ano_censo');
+           .field('simcaq_docente_agregada.ano_censo', 'year')
+           .from('simcaq_docente_agregada')
+           .group('simcaq_docente_agregada.ano_censo')
+           .order('simcaq_docente_agregada.ano_censo');
 
     next();
 }, rqf.build(), query, id2str.transform(), (req, res, next) =>  {
@@ -401,13 +373,13 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.dims.state = true;
     req.dims.city = true;
     req.dims.education_level_short = true;
-    req.dims.integral_time = true;
+    //req.dims.integral_time = true;
     req.sql.field('COUNT(*)', 'total')
-           .field('matricula.ano_censo', 'year')
-           .from('matricula')
-           .group('matricula.ano_censo')
-           .order('matricula.ano_censo')
-           .where('((matricula.tipo<=3 OR matricula.tipo IS NULL) AND (matricula.tipo_atendimento_turma IS NULL OR matricula.tipo_atendimento_turma <= 2) AND matricula.turma_turno_id <> 99)');
+           .field('simcaq_matricula_agregada.ano_censo', 'year')
+           .from('simcaq_matricula_agregada')
+           .group('simcaq_matricula_agregada.ano_censo')
+           .order('simcaq_matricula_agregada.ano_censo');
+           //.where('((simcaq_matricula_agregada.tipo<=3 OR simcaq_matricula_agregada.tipo IS NULL) AND (simcaq_matricula_agregada.tipo_atendimento_turma IS NULL OR simcaq_matricula_agregada.tipo_atendimento_turma <= 2) AND simcaq_matricula_agregada.turma_turno_id <> 99)');
     next();
 }, rqf.build() ,query, id2str.transform(), (req, res, next) => {
     // constrói objeto de tempo integral e calcula diagnósticos
@@ -566,7 +538,6 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                     ti++;
                 
                 if (ti === req.teacher.length) {
-                    console.log(classroom[id_attribute], "not found")
                     while (classroom[id_attribute] === enrollments[j][id_attribute])
                         enrollments.splice(j, 1)
                     ti = old_ti; 
-- 
GitLab


From 331ec294df84190b6d4e6b2517094820bf9205c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Wed, 31 May 2023 09:43:09 -0300
Subject: [PATCH 090/123] Add changes to classroom count v2

---
 src/libs/routes_v2/aux               | 1 -
 src/libs/routes_v2/classroomCount.js | 9 +++++----
 2 files changed, 5 insertions(+), 5 deletions(-)
 delete mode 100644 src/libs/routes_v2/aux

diff --git a/src/libs/routes_v2/aux b/src/libs/routes_v2/aux
deleted file mode 100644
index c0b4cc5f..00000000
--- a/src/libs/routes_v2/aux
+++ /dev/null
@@ -1 +0,0 @@
-SELECT COUNT(*) AS "total", simcaq_matricula_agregada.ano_censo AS "year", estado.nome AS "state_name", estado.id AS "state_id", municipio.nome AS "city_name", municipio.id AS "city_id", simcaq_matricula_agregada.etapa_resumida AS "education_level_short_id", simcaq_matricula_agregada.tempo_integral AS "integral_time_id" FROM simcaq_matricula_agregada INNER JOIN estado ON (simcaq_matricula_agregada.estado_id=estado.id) INNER JOIN municipio ON (simcaq_matricula_agregada.municipio_id=municipio.id) WHERE (simcaq_matricula_agregada.ano_censo >= 2021 ) AND (simcaq_matricula_agregada.ano_censo <= 2021 ) GROUP BY simcaq_matricula_agregada.ano_censo, estado.nome, estado.id, municipio.nome, municipio.id, simcaq_matricula_agregada.etapa_resumida, simcaq_matricula_agregada.tempo_integral ORDER BY simcaq_matricula_agregada.ano_censo ASC, estado.nome ASC, estado.id ASC, municipio.nome ASC, municipio.id ASC, simcaq_matricula_agregada.etapa_resumida ASC, simcaq_matricula_agregada.tempo_integral ASC
diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index b4ba338a..1c3bb6b3 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -250,9 +250,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     .field('sum(noite_total)', 'total_night')
     .field("'Brasil'", 'name')
     .field('simcaq_matricula_por_localizacao.ano_censo', 'year')
+    .field('simcaq_matricula_por_localizacao.serie_ano_id', 'school_year_id')
     .from('simcaq_matricula_por_localizacao')
     .where('simcaq_matricula_por_localizacao.serie_ano_id < 15')
     .group('simcaq_matricula_por_localizacao.ano_censo')
+    .group('simcaq_matricula_por_localizacao.serie_ano_id')
     .order('simcaq_matricula_por_localizacao.ano_censo')
 
     next();
@@ -387,7 +389,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.integral_time = {}
     for (let i = 0; i < integral_time_result.length; ++i){
         // Se cidade não foi criada, cria
-        let integral_time = integral_time_result[i];
+        let integral_time = integral_time_result[i]
         let code = '' + integral_time.year + integral_time.city_id
         if (req.dims.school) code = code + integral_time.school_id
 
@@ -591,13 +593,12 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 let enrollmentEducationLevel = req.educationSchoolYear[enrollment.school_year_id];
                 // Se não há um número de alunos por turna para a etapa de ensino, ignoramos a entrada
 
-                if(enrollmentEducationLevel.numberStudentClass == null) continue;
+                if(typeof enrollmentEducationLevel.numberStudentClass == 'undefined' || enrollmentEducationLevel.numberStudentClass == null) continue;
                 
                 // Adiciona nível de educação para município/escola
                 let educationLevel = null;
                 if(!educationLevelSet.has(enrollmentEducationLevel.id)) { // cria e insere ordenadamente novo education level
                     educationLevelSet.add(enrollmentEducationLevel.id);
-                    
                     let itHash = '' + enrollment.year + enrollment.city_id
                     if (req.dims.school) itHash += enrollment.school_id
     
@@ -664,7 +665,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 let currentSchoolYear = null;
                 if(enrollmentEducationLevel.id == 1){
                     let schoolYearHash = '' + enrollment.year + enrollment.city_id + enrollment.location_id + enrollment.school_year_id;
-                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.shcool_id
+                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.school_id
 
                     if(schoolYearSet.has(schoolYearHash)) { // Busca a série escolar
                         let k = 0;
-- 
GitLab


From 73ce2f5f6220c7b2a50ee5ffd26ad41abada6a55 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Wed, 31 May 2023 11:36:17 -0300
Subject: [PATCH 091/123] update classroomcount v2

---
 src/libs/routes_v2/classroomCount.js | 25 ++++++-------------------
 1 file changed, 6 insertions(+), 19 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 1c3bb6b3..1058aa41 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -66,21 +66,6 @@ rqf.addField({
         foreign: 'municipio_id',
         foreignTable: '@'
     }
-}, 'dims').addValueToField({
-    name: 'cityTeacher',
-    table: 'municipio',
-    tableField: ['nome', 'id'],
-    resultField: ['city_name', 'city_id'],
-    where: {
-        relation: '=',
-        type: 'integer',
-        field: 'id'
-    },
-    join: {
-        primary: 'id',
-        foreign: 'escola_cidade_id',
-        foreignTable: '@'
-    }
 }, 'dims').addValueToField({
     name: 'state',
     table: 'estado',
@@ -354,7 +339,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         req.teacherCalc = true;
     }
 
-    req.dims.cityTeacher = true;
+    req.dims.city = true;
     req.dims.formation_level = true;
     req.sql.field('SUM(simcaq_docente_agregada.num_docentes)', 'total')
            .field("'Brasil'", 'name')
@@ -534,11 +519,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 currentClassroomObj = obj;
 
                 var id_attribute = req.dims.school ? "school_id" : "city_id"
-                
                 var old_ti = ti;
-                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) // match da tabela de professores.
+                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) { // match da tabela de professores.
                     ti++;
-                
+                }
+
                 if (ti === req.teacher.length) {
                     while (classroom[id_attribute] === enrollments[j][id_attribute])
                         enrollments.splice(j, 1)
@@ -1141,6 +1126,8 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         })
     }
 
+    console.log('fim')
+
     next();
 }, response('classroom_count'));
 
-- 
GitLab


From 1e4ed735f7d8b8755f82f9e4d94dc2ae2d168210 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 2 Jun 2023 11:02:59 -0300
Subject: [PATCH 092/123] rewrite queries for classroom_count

---
 src/libs/routes_v2/classroomCount.js | 94 +++++++++++++++++++++++++---
 src/libs/routes_v2/infrastructure.js | 29 +++++----
 2 files changed, 102 insertions(+), 21 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 1058aa41..112d70db 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -228,9 +228,32 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
 
     req.dims.state = true;
     req.dims.city = true;
-    req.dims.school_year = true;
+    //req.dims.school_year = true;
     req.dims.location = true;
 
+    /*
+    select
+    sum(dia_total),
+    sum(noite_total),
+    'Brasil' as name,
+    ano_censo,
+    serie_ano_id,
+    estado_id,
+    municipio_id,
+    localizacao_id
+    from
+    simcaq_matricula_por_localizacao
+    where
+    serie_ano_id < 15
+    group by
+    name,
+    ano_censo,
+    serie_ano_id,
+    estado_id,
+    municipio_id,
+    localizacao_id
+    */
+
     req.sql.field('sum(dia_total)', 'total_day')
     .field('sum(noite_total)', 'total_night')
     .field("'Brasil'", 'name')
@@ -277,19 +300,42 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     next();
 }, rqf.parse(), (req, res, next) => {
 
+    /*
+    select
+    sum(escola_agregada.num_salas_utilizadas) as soma,
+    'Brasil' as name,
+    ano_censo,
+    estado_id,
+    municipio_id,
+    localizacao_id,
+    local_func_predio_escolar
+    from escola_agregada
+    where
+    situacao_de_funcionamento = 1 and
+    dependencia_adm_id IN (2, 3) and
+    escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1
+    group by
+    name,
+    ano_censo,
+    estado_id,
+    municipio_id,
+    localizacao_id,
+    local_func_predio_escolar
+    */
+
     req.dims.state = true;
     req.dims.city = true;
     req.dims.location = true;
     req.dims.school_building = true;
-
-    req.sql.field('SUM(escola_agregada.qtde_salas_utilizadas_dentro)', 'total')
+    
+    req.sql.field('SUM(escola_agregada.num_salas_utilizadas)', 'total')
     .field("'Brasil'", 'name')
     .field('escola_agregada.ano_censo', 'year')
     .from('escola_agregada')
     .group('escola_agregada.ano_censo')
     .order('escola_agregada.ano_censo')
-    .where('escola_agregada.situacao_funcionamento_pareada = 1')
-    .where('escola_agregada.dependencia_adm_id < 4') 
+    .where('escola_agregada.situacao_de_funcionamento = 1')
+    .where('escola_agregada.dependencia_adm_id IN (2,3)') 
     .where('escola_agregada.ensino_regular = 1 OR escola_agregada.ensino_eja = 1 OR escola_agregada.educacao_profissional = 1');
 
     next();
@@ -339,11 +385,22 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         req.teacherCalc = true;
     }
 
-    req.dims.city = true;
-    req.dims.formation_level = true;
-    req.sql.field('SUM(simcaq_docente_agregada.num_docentes)', 'total')
+    /*
+    select num_docentes,
+    'Brasil' as name,
+    ano_censo,
+    escola_estado_id,
+    municipio_id
+    from simcaq_docente_agregada
+    */
+
+    //req.dims.city = true;
+    //req.dims.state = true;
+    req.sql.field('simcaq_docente_agregada.num_docentes', 'total')
            .field("'Brasil'", 'name')
            .field('simcaq_docente_agregada.ano_censo', 'year')
+           .field('escola_estado_id', 'state_id')
+           .field('municipio_id', 'city_id')
            .from('simcaq_docente_agregada')
            .group('simcaq_docente_agregada.ano_censo')
            .order('simcaq_docente_agregada.ano_censo');
@@ -357,16 +414,33 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.resetSql();
     next();
 }, rqf.parse(), (req, res, next) => {
+    /*
+    select
+    SUM(num_matriculas) as num_matriculas,
+    ano_censo,
+    estado_id,
+    municipio_id,
+    etapa_resumida,
+    tempo_integral
+    from simcaq_matricula_agregada
+    group by
+    ano_censo,
+    estado_id,
+    municipio_id,
+    etapa_resumida,
+    tempo_integral
+    */
     req.dims.state = true;
     req.dims.city = true;
     req.dims.education_level_short = true;
     //req.dims.integral_time = true;
-    req.sql.field('COUNT(*)', 'total')
+    req.sql.field('SUM(num_matriculas)', 'total')
            .field('simcaq_matricula_agregada.ano_censo', 'year')
+           .field('simcaq_matricula_agregada.tempo_integral', 'integral_time')
            .from('simcaq_matricula_agregada')
            .group('simcaq_matricula_agregada.ano_censo')
+           .group('simcaq_matricula_agregada.tempo_integral')
            .order('simcaq_matricula_agregada.ano_censo');
-           //.where('((simcaq_matricula_agregada.tipo<=3 OR simcaq_matricula_agregada.tipo IS NULL) AND (simcaq_matricula_agregada.tipo_atendimento_turma IS NULL OR simcaq_matricula_agregada.tipo_atendimento_turma <= 2) AND simcaq_matricula_agregada.turma_turno_id <> 99)');
     next();
 }, rqf.build() ,query, id2str.transform(), (req, res, next) => {
     // constrói objeto de tempo integral e calcula diagnósticos
diff --git a/src/libs/routes_v2/infrastructure.js b/src/libs/routes_v2/infrastructure.js
index cc4c5931..01cc9264 100644
--- a/src/libs/routes_v2/infrastructure.js
+++ b/src/libs/routes_v2/infrastructure.js
@@ -335,7 +335,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     // Laboratório de informática
     let allInfLab = allSchools.clone();
     allInfLab.where('escola_agregada.func_predio_escolar = 1')
-    .where('escola_agregada.reg_fund_ai = 1 OR escola_agregada.reg_fund_af = 1 OR escola_agregada.reg_medio_medio = 1 OR escola_agregada.reg_medio_integrado = 1 OR escola_agregada.reg_medio_normal = 1 OR escola_agregada.ensino_eja_fund = 1 OR escola_agregada.ensino_eja_medio = 1 OR escola_agregada.ensino_eja_prof = 1');
+    .where('etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1');
     req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1;
 
     let haveInfLab = allInfLab.clone();
@@ -347,13 +347,13 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1;
 
     let haveScienceLab = allScienceLab.clone();
-    haveScienceLab.where('escola_agregada.lab_ciencias = 1');
+    haveScienceLab.where('escola_agregada.lab_ciencias = 1 AND (etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1;
 
     // Parque infantil
     let allKidsPark = allSchools.clone();
     allKidsPark.where('escola_agregada.func_predio_escolar = 1')
-    .where('escola_agregada.reg_infantil_creche = 1 OR escola_agregada.reg_infantil_preescola = 1 OR escola_agregada.reg_fund_ai = 1 OR escola_agregada.esp_infantil_creche = 1 OR escola_agregada.esp_exclusiva_creche = 1 OR escola_agregada.reg_esp_exclusiva_fund_ai = 1');
+    .where('escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1');
     req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1;
 
     let haveKidsPark = allKidsPark.clone();
@@ -372,27 +372,27 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
 
     // Quadra
     let allSportsCourt = allScienceLab.clone();
-    allSportsCourt.where('escola_agregada.localizacao_id = 1');
+    allSportsCourt.where('escola_agregada.localizacao_id = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1;
 
     let haveSportsCourt = allSportsCourt.clone();
-    haveSportsCourt.where('escola_agregada.quadra_esportes = 1');
+    haveSportsCourt.where('escola_agregada.quadra_esportes = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1;
 
     // Quadra coberta
     req.queryIndex.allCoveredSportsCourt = req.queryIndex.allSportsCourt;
 
     let haveCoveredSportsCourt = allSportsCourt.clone();
-    haveCoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 1');
+    haveCoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveCoveredSportsCourt = req.querySet.push(haveCoveredSportsCourt) - 1;
 
     // Quadra Descoberta
     let allUncoveredSportsCourt = allSportsCourt.clone();
-    allUncoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 0');
+    allUncoveredSportsCourt.where('escola_agregada.quadra_esportes_coberta = 0 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.allUncoveredSportsCourt = req.querySet.push(allUncoveredSportsCourt) - 1;
 
     let haveUncoveredSportsCourt = allUncoveredSportsCourt.clone();
-    haveUncoveredSportsCourt.where('escola_agregada.quadra_esportes_descoberta = 1');
+    haveUncoveredSportsCourt.where('escola_agregada.quadra_esportes_descoberta = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveUncoveredSportsCourt = req.querySet.push(haveUncoveredSportsCourt) - 1;
 
     // Sala de direção
@@ -445,14 +445,14 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInternet = req.queryIndex.allLibrariesReadingRoom;
 
     let haveInternet = allLibrariesReadingRoom.clone();
-    haveInternet.where('escola_agregada.internet = 1');
+    haveInternet.where('escola_agregada.internet = 1 AND localizacao_id = 2');
     req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1;
 
     // Internet banda larga
     req.queryIndex.allBroadbandInternet = req.queryIndex.allLibraries;
 
     let haveBroadbandInternet = allLibraries.clone();
-    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1');
+    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1 AND localizacao_id = 1');
     req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1;
 
     // Banheiro dentro do prédio
@@ -466,7 +466,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInsideKidsBathroom = req.queryIndex.allKidsPark;
 
     let haveInsideKidsBathroom = allKidsPark.clone();
-    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1');
+    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1 AND (escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1) AND localizacao_id IN (1, 2)');
     req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1;
 
     // Fornecimento de energia
@@ -518,6 +518,13 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     haveAdaptedBuilding.where('escola_agregada.dependencias_pne = 1');
     req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1;
 
+    // Adicionar pátio
+    /*
+    patio = 1 ou 2 -- patio existe, 1 -- a ser coberto, 2 -- é coberto
+    func_predio_escolar = 1
+    localizacao_id = 1 ou 2
+    */
+
     next();
 }, multiQuery, (req, res, next) => {
     // Faz o matching entre os resultados
-- 
GitLab


From 8e84c1dd432df2b75b93555b833510ae8f109856 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Mon, 5 Jun 2023 10:20:18 -0300
Subject: [PATCH 093/123] classroom count v2 working but does not return data

---
 src/libs/routes_v2/classroomCount.js | 41 +++++-----------------------
 1 file changed, 7 insertions(+), 34 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 112d70db..47ea5e78 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -231,28 +231,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     //req.dims.school_year = true;
     req.dims.location = true;
 
-    /*
-    select
-    sum(dia_total),
-    sum(noite_total),
-    'Brasil' as name,
-    ano_censo,
-    serie_ano_id,
-    estado_id,
-    municipio_id,
-    localizacao_id
-    from
-    simcaq_matricula_por_localizacao
-    where
-    serie_ano_id < 15
-    group by
-    name,
-    ano_censo,
-    serie_ano_id,
-    estado_id,
-    municipio_id,
-    localizacao_id
-    */
+
 
     req.sql.field('sum(dia_total)', 'total_day')
     .field('sum(noite_total)', 'total_night')
@@ -386,21 +365,19 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     }
 
     /*
-    select num_docentes,
+    select sum(num_docentes),
     'Brasil' as name,
     ano_censo,
-    escola_estado_id,
+    estado_id,
     municipio_id
     from simcaq_docente_agregada
     */
 
-    //req.dims.city = true;
-    //req.dims.state = true;
-    req.sql.field('simcaq_docente_agregada.num_docentes', 'total')
+    req.dims.city = true;
+    req.dims.state = true;
+    req.sql.field('SUM(simcaq_docente_agregada.num_docentes)', 'total')
            .field("'Brasil'", 'name')
            .field('simcaq_docente_agregada.ano_censo', 'year')
-           .field('escola_estado_id', 'state_id')
-           .field('municipio_id', 'city_id')
            .from('simcaq_docente_agregada')
            .group('simcaq_docente_agregada.ano_censo')
            .order('simcaq_docente_agregada.ano_censo');
@@ -433,13 +410,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.dims.state = true;
     req.dims.city = true;
     req.dims.education_level_short = true;
-    //req.dims.integral_time = true;
+    req.dims.integral_time = true;
     req.sql.field('SUM(num_matriculas)', 'total')
            .field('simcaq_matricula_agregada.ano_censo', 'year')
-           .field('simcaq_matricula_agregada.tempo_integral', 'integral_time')
            .from('simcaq_matricula_agregada')
            .group('simcaq_matricula_agregada.ano_censo')
-           .group('simcaq_matricula_agregada.tempo_integral')
            .order('simcaq_matricula_agregada.ano_censo');
     next();
 }, rqf.build() ,query, id2str.transform(), (req, res, next) => {
@@ -1200,8 +1175,6 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
         })
     }
 
-    console.log('fim')
-
     next();
 }, response('classroom_count'));
 
-- 
GitLab


From 7631b576f431e672a97dad6442dd51741189682b Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Mon, 5 Jun 2023 10:28:57 -0300
Subject: [PATCH 094/123] trying to use the old version of classroom count

---
 src/libs/routes_v2/classroomCount.js | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/libs/routes_v2/classroomCount.js b/src/libs/routes_v2/classroomCount.js
index 47ea5e78..ea649bbf 100644
--- a/src/libs/routes_v2/classroomCount.js
+++ b/src/libs/routes_v2/classroomCount.js
@@ -228,7 +228,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
 
     req.dims.state = true;
     req.dims.city = true;
-    //req.dims.school_year = true;
+    req.dims.school_year = true;
     req.dims.location = true;
 
 
@@ -423,7 +423,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     req.integral_time = {}
     for (let i = 0; i < integral_time_result.length; ++i){
         // Se cidade não foi criada, cria
-        let integral_time = integral_time_result[i]
+        let integral_time = integral_time_result[i];
         let code = '' + integral_time.year + integral_time.city_id
         if (req.dims.school) code = code + integral_time.school_id
 
@@ -568,12 +568,13 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 currentClassroomObj = obj;
 
                 var id_attribute = req.dims.school ? "school_id" : "city_id"
+                
                 var old_ti = ti;
-                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) { // match da tabela de professores.
+                while (ti < req.teacher.length && req.teacher[ti][id_attribute] !== classroom[id_attribute]) // match da tabela de professores.
                     ti++;
-                }
-
+                
                 if (ti === req.teacher.length) {
+                    console.log(classroom[id_attribute], "not found")
                     while (classroom[id_attribute] === enrollments[j][id_attribute])
                         enrollments.splice(j, 1)
                     ti = old_ti; 
@@ -627,12 +628,13 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 let enrollmentEducationLevel = req.educationSchoolYear[enrollment.school_year_id];
                 // Se não há um número de alunos por turna para a etapa de ensino, ignoramos a entrada
 
-                if(typeof enrollmentEducationLevel.numberStudentClass == 'undefined' || enrollmentEducationLevel.numberStudentClass == null) continue;
+                if(enrollmentEducationLevel.numberStudentClass == null) continue;
                 
                 // Adiciona nível de educação para município/escola
                 let educationLevel = null;
                 if(!educationLevelSet.has(enrollmentEducationLevel.id)) { // cria e insere ordenadamente novo education level
                     educationLevelSet.add(enrollmentEducationLevel.id);
+                    
                     let itHash = '' + enrollment.year + enrollment.city_id
                     if (req.dims.school) itHash += enrollment.school_id
     
@@ -699,7 +701,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 let currentSchoolYear = null;
                 if(enrollmentEducationLevel.id == 1){
                     let schoolYearHash = '' + enrollment.year + enrollment.city_id + enrollment.location_id + enrollment.school_year_id;
-                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.school_id
+                    if (req.dims.shool) schoolYearHash = schoolYearHash + enrollment.shcool_id
 
                     if(schoolYearSet.has(schoolYearHash)) { // Busca a série escolar
                         let k = 0;
-- 
GitLab


From 6b6f1e252d459b70a7a8d447d06e3459cc021353 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Tue, 6 Jun 2023 11:52:12 -0300
Subject: [PATCH 095/123] update filters of school_infrastructure

---
 src/libs/routes_v2/infrastructure.js       |  6 -----
 src/libs/routes_v2/schoolInfrastructure.js | 28 +++++++++++-----------
 2 files changed, 14 insertions(+), 20 deletions(-)

diff --git a/src/libs/routes_v2/infrastructure.js b/src/libs/routes_v2/infrastructure.js
index 01cc9264..89d7e278 100644
--- a/src/libs/routes_v2/infrastructure.js
+++ b/src/libs/routes_v2/infrastructure.js
@@ -518,12 +518,6 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     haveAdaptedBuilding.where('escola_agregada.dependencias_pne = 1');
     req.queryIndex.haveAdaptedBuilding = req.querySet.push(haveAdaptedBuilding) - 1;
 
-    // Adicionar pátio
-    /*
-    patio = 1 ou 2 -- patio existe, 1 -- a ser coberto, 2 -- é coberto
-    func_predio_escolar = 1
-    localizacao_id = 1 ou 2
-    */
 
     next();
 }, multiQuery, (req, res, next) => {
diff --git a/src/libs/routes_v2/schoolInfrastructure.js b/src/libs/routes_v2/schoolInfrastructure.js
index da4d4593..00b87e03 100644
--- a/src/libs/routes_v2/schoolInfrastructure.js
+++ b/src/libs/routes_v2/schoolInfrastructure.js
@@ -387,7 +387,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     // ( reg_fund_ai_t1=1 | reg_fund_af_t1=1 | reg_medio_medio_t1=1 | ensino_eja_fund= 1 |     ensino_eja_medio= 1 | ensino_eja_prof= 1 | esp_eja_fund=1 | 
     // esp_eja_medio=1 | ensino_esp_exclusiva_eja_prof=1) então conta id
     let allInfLab = allSchools.clone();
-    allInfLab.where('reg_fund_ai_t1=1 OR reg_fund_af_t1=1 OR reg_medio_medio_t1=1 OR ensino_eja_fund=1 OR ensino_eja_medio=1 OR ensino_eja_prof=1 OR esp_eja_fund=1 OR esp_eja_medio=1 OR ensino_esp_exclusiva_eja_prof=1');
+    allInfLab.where('etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1');
     req.queryIndex.allInfLab = req.querySet.push(allInfLab) - 1;
 
     let haveInfLab = allInfLab.clone();
@@ -404,7 +404,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allScienceLab = req.querySet.push(allScienceLab) - 1;
 
     let haveScienceLab = allScienceLab.clone();
-    haveScienceLab.where('escola_agregada.lab_ciencias = true');
+    haveScienceLab.where('escola_agregada.lab_ciencias = 1 AND (etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveScienceLab = req.querySet.push(haveScienceLab) - 1;
 
     let needScienceLab = allScienceLab.clone();
@@ -415,7 +415,7 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     // Se (situacao_de_funcionamento=1) and (ensino_regular=1 OR ensino_eja=1 OR educacao_profissional=1) and 
     // (local_func_predio_escolar=1) and (dependencia_adm_id<=3) and (reg_infantil_creche_t1=1 or reg_infantil_preescola_t1=1 or reg_fund_ai_t1=1) então conta id
     let allKidsPark = allSchools.clone();
-    allKidsPark.where('reg_infantil_creche_t1=1 OR reg_infantil_preescola_t1=1 OR reg_fund_ai_t1=1');
+    allKidsPark.where('escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1');
     req.queryIndex.allKidsPark = req.querySet.push(allKidsPark) - 1;
 
     let haveKidsPark = allKidsPark.clone();
@@ -441,15 +441,15 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
 
     // Quadra de esportes
     let allSportsCourt = allSchools.clone();
-    allSportsCourt.where('reg_fund_ai_t1=1  or reg_fund_af_t1=1 or  reg_medio_medio_t1=1  or  ensino_eja_fund= 1 or ensino_eja_medio= 1 or ensino_eja_prof= 1 or esp_eja_fund=1 or esp_eja_medio=1 or ensino_esp_exclusiva_eja_prof=1');
+    allSportsCourt.where('escola_agregada.localizacao_id = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.allSportsCourt = req.querySet.push(allSportsCourt) - 1;
 
     let haveSportsCourt = allSportsCourt.clone();
-    haveSportsCourt.where('escola_agregada.quadra_esportes = 1');
+    haveSportsCourt.where('escola_agregada.quadra_esportes = 1 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.haveSportsCourt = req.querySet.push(haveSportsCourt) - 1;
 
     let needSportsCourt = allSportsCourt.clone();
-    needSportsCourt.where('escola_agregada.quadra_esportes = 0');
+    needSportsCourt.where('escola_agregada.quadra_esportes = 0 AND (etapa_en_fundamental_anos_iniciais = 1 OR etapa_en_fundamental_anos_finais = 1 OR etapa_en_medio = 1)');
     req.queryIndex.needSportsCourt = req.querySet.push(needSportsCourt) - 1;
 
     // Quadras a serem cobertas
@@ -468,11 +468,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allCourtyard = req.queryIndex.allSchools;
 
     let haveCourtyard = allSchools.clone();
-    haveCourtyard.where('escola_agregada.patio = 1 OR escola_agregada.patio = 2');
+    haveCourtyard.where('escola_agregada.func_predio_escolar = 1 AND (escola_agregada.patio = 1 OR escola_agregada.patio = 2)');
     req.queryIndex.haveCourtyard = req.querySet.push(haveCourtyard) - 1;
 
     let needCourtyard = allSchools.clone();
-    needCourtyard.where('escola_agregada.patio = 0');
+    needCourtyard.where('escola_agregada.func_predio_escolar = 1 AND escola_agregada.patio = 0');
     req.queryIndex.needCourtyard = req.querySet.push(needCourtyard) - 1;
 
     // Pátios a serem cobertos
@@ -554,11 +554,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInternet = req.queryIndex.allCountrySchools;
 
     let haveInternet = allCountrySchools.clone();
-    haveInternet.where('escola_agregada.internet = 1');
+    haveInternet.where('escola_agregada.internet = 1 AND localizacao_id = 2');
     req.queryIndex.haveInternet = req.querySet.push(haveInternet) - 1;
 
     let needInternet = allCountrySchools.clone();
-    needInternet.where('escola_agregada.internet = 0');
+    needInternet.where('escola_agregada.internet = 0 AND localizacao_id = 2');
     req.queryIndex.needInternet = req.querySet.push(needInternet) - 1;
 
     // Internet banda larga
@@ -567,11 +567,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allBroadbandInternet = req.queryIndex.allUrbanSchools;
 
     let haveBroadbandInternet = allUrbanSchools.clone();
-    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1');
+    haveBroadbandInternet.where('escola_agregada.internet_banda_larga = 1 AND localizacao_id = 1');
     req.queryIndex.haveBroadbandInternet = req.querySet.push(haveBroadbandInternet) - 1;
 
     let needBroadbandInternet = allUrbanSchools.clone();
-    needBroadbandInternet.where('escola_agregada.internet_banda_larga = 0');
+    needBroadbandInternet.where('escola_agregada.internet_banda_larga = 0 AND localizacao_id = 1');
     req.queryIndex.needBroadbandInternet = req.querySet.push(needBroadbandInternet) - 1;
 
     // Banheiro
@@ -593,11 +593,11 @@ infrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
     req.queryIndex.allInsideKidsBathroom = req.querySet.push(allInsideKidsBathroom) - 1;
 
     let haveInsideKidsBathroom = allInsideKidsBathroom.clone();
-    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1');
+    haveInsideKidsBathroom.where('escola_agregada.sanitario_ei = 1 AND (escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1) AND localizacao_id IN (1, 2)');
     req.queryIndex.haveInsideKidsBathroom = req.querySet.push(haveInsideKidsBathroom) - 1;
 
     let needInsideKidsBathroom = allInsideKidsBathroom.clone();
-    needInsideKidsBathroom.where('escola_agregada.sanitario_ei = 0');
+    needInsideKidsBathroom.where('escola_agregada.sanitario_ei = 0 AND (escola_agregada.etapa_ed_infantil_creche = 1 OR escola_agregada.etapa_en_fundamental_anos_iniciais = 1) AND localizacao_id IN (1, 2)');
     req.queryIndex.needInsideKidsBathroom = req.querySet.push(needInsideKidsBathroom) - 1;
 
     // Fornecimento de energia
-- 
GitLab


From 2007841fe42e81ee85f3a0d643e5da840982111d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 22 Jun 2023 09:18:15 -0300
Subject: [PATCH 096/123] Changes to simcaq infrastructure

---
 src/libs/convert/scholarDependency.js         | 76 ++++++++++++++++
 src/libs/middlewares/id2str.js                |  4 +-
 src/libs/routes_v2/api.js                     |  3 +
 .../routes_v2/simcaqSchoolInfrastructure.js   | 87 +++++++++++++++++++
 4 files changed, 169 insertions(+), 1 deletion(-)
 create mode 100644 src/libs/convert/scholarDependency.js
 create mode 100644 src/libs/routes_v2/simcaqSchoolInfrastructure.js

diff --git a/src/libs/convert/scholarDependency.js b/src/libs/convert/scholarDependency.js
new file mode 100644
index 00000000..25684b9b
--- /dev/null
+++ b/src/libs/convert/scholarDependency.js
@@ -0,0 +1,76 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+module.exports = function scholarDependency(id) {
+    switch (id) {
+        case 1:
+            return "Biblioteca";
+        case 2:
+            return "Sala de leitura";
+        case 3:
+            return "Laboratório de informática";
+        case 4:
+            return "Laboratório de ciências";
+        case 5:
+            return "Parque infantil";
+        case 6:
+            return "Quadra poliesportiva";
+        case 7:
+            return "Quadras a serem cobertas";
+        case 8:
+            return "Pátio";
+        case 9:
+            return "Pátios a serem cobertos";
+        case 10:
+            return "Sala de direção";
+        case 11:
+            return "Secretaria";
+        case 12:
+            return "Sala de professores";
+        case 13:
+            return "Cozinha";
+        case 14:
+            return "Despensa";
+        case 15:
+            return "Almoxarifado";
+        case 16:
+            return "Internet";
+        case 17:
+            return "Internet banda larga";
+        case 18:
+            return "Banheiro dentro do prédio";
+        case 19:
+            return "Banheiro adequado para educação infantil dentro do prédio";
+        case 20:
+            return "Fornecimento de energia";
+        case 21:
+            return "Abastecimento de água";
+        case 22:
+            return "Água filtrada";
+        case 23:
+            return "Coleta de esgoto";
+        case 24:
+            return "Dependências adaptadas para pessoas com deficiências";
+        case 25:
+            return "Banheiros adaptados para pessoas com deficiências";
+        default:
+            return "Dependência escolar desconhecida";
+    }
+}
\ No newline at end of file
diff --git a/src/libs/middlewares/id2str.js b/src/libs/middlewares/id2str.js
index 39d819aa..cb549d3d 100644
--- a/src/libs/middlewares/id2str.js
+++ b/src/libs/middlewares/id2str.js
@@ -100,6 +100,7 @@ const admDependencyPub = require(`${libs}/convert/admDependencyPub`);
 const supplyDimension = require(`${libs}/convert/supplyDimension`);
 const type = require(`${libs}/convert/type`);
 const level = require(`${libs}/convert/level`);
+const scholarDependency = require(`${libs}/convert/scholarDependency`);
 
 const ids = {
     gender_id: gender,
@@ -191,7 +192,8 @@ const ids = {
     shift_id: shift,
     supply_dimension_id: supplyDimension,
     type_id: type,
-    level_id: level
+    level_id: level,
+    scholar_dependency_id: scholarDependency
 };
 
 function transform(removeId=false) {
diff --git a/src/libs/routes_v2/api.js b/src/libs/routes_v2/api.js
index 5c00d3fe..a8e1e1b7 100644
--- a/src/libs/routes_v2/api.js
+++ b/src/libs/routes_v2/api.js
@@ -160,6 +160,8 @@ const simcaqAggregatedEnrollment = require(`${libs}/routes_v2/simcaqAggregatedEn
 
 const simcaqTeacherCityPlan = require(`${libs}/routes_v2/simcaqTeacherCityPlan`);
 
+const simcaqSchoolInfrastructure = require(`${libs}/routes_v2/simcaqSchoolInfrastructure`);
+
 api.get('/', (req, res) => {
     res.json({ msg: 'SimCAQ API v2 is running' });
 });
@@ -231,5 +233,6 @@ api.use('/simcaq_number_of_teachers', simcaqNumberOfTeachers);
 api.use('/simcaq_enrollment_projection', simcaqEnrollmentProjection);
 api.use('/simcaq_aggregated_enrollment', simcaqAggregatedEnrollment);
 api.use('/simcaq_teacher_city_plan', simcaqTeacherCityPlan);
+api.use('/simcaq_school_infrastructure', simcaqSchoolInfrastructure);
 
 module.exports = api;
diff --git a/src/libs/routes_v2/simcaqSchoolInfrastructure.js b/src/libs/routes_v2/simcaqSchoolInfrastructure.js
new file mode 100644
index 00000000..f10022cb
--- /dev/null
+++ b/src/libs/routes_v2/simcaqSchoolInfrastructure.js
@@ -0,0 +1,87 @@
+/*
+Copyright (C) 2016 Centro de Computacao Cientifica e Software Livre
+Departamento de Informatica - Universidade Federal do Parana - C3SL/UFPR
+
+This file is part of simcaq-node.
+
+simcaq-node is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+simcaq-node is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with simcaq-node.  If not, see <https://www.gnu.org/licenses/>.
+*/
+
+const express = require('express');
+
+const simcaqSchoolInfrastructureApp = express.Router();
+
+const libs = `${process.cwd()}/libs`;
+
+const squel = require('squel');
+
+const query = require(`${libs}/middlewares/query`).query;
+
+const response = require(`${libs}/middlewares/response`);
+
+const ReqQueryFields = require(`${libs}/middlewares/reqQueryFields`);
+
+const reqBody = require(`${libs}/middlewares/reqBody`);
+
+const config = require(`${libs}/config`); 
+
+const cache = require('apicache').options({ debug: config.debug, statusCodes: {include: [200]} }).middleware;
+
+let rqf = new ReqQueryFields();
+
+const id2str = require(`${libs}/middlewares/id2str`);
+
+simcaqSchoolInfrastructureApp.use(cache('15 day'));
+
+rqf.addField({
+    name: 'filter',
+    field: false,
+    where: true
+}).addField({
+    name: 'dims',
+    field: true,
+    where: false
+}).addValue({
+    name: 'year',
+    table: 'simcaq_school_infrastructure',
+    tableField: 'ano_censo',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'ano_censo'
+    }
+}).addValue({
+    name: 'scholar_dependency',
+    table: 'simcaq_school_infrastructure',
+    tableField: 'scholar_dependency_id',
+    where: {
+        relation: '=',
+        type: 'integer',
+        field: 'scholar_dependency_id'
+    }
+});
+
+simcaqSchoolInfrastructureApp.get('/', rqf.parse(), rqf.build(), (req, res, next) => {
+    req.sql.from('simcaq_school_infrastructure')
+        .field('simcaq_school_infrastructure.ano_censo', 'year')
+        .field('simcaq_school_infrastructure.scholar_dependency_id', 'scholar_dependency_id')
+        .field('SUM(simcaq_school_infrastructure.total_schools)', 'total_schools')
+        .field('SUM(total_no_dependency)', 'total_schools_without_dependency')
+        .field('SUM(total_with_dependency)', 'total_schools_with_dependency')
+        .group('simcaq_school_infrastructure.ano_censo')
+        .group('simcaq_school_infrastructure.scholar_dependency_id');
+    next();
+}, query, id2str.transform(), response('simcaqSchoolInfrastructure'));
+
+module.exports = simcaqSchoolInfrastructureApp;
-- 
GitLab


From 0ff1fc58bd045a84cf1f084804912ce35a3f33b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 29 Jun 2023 10:45:25 -0300
Subject: [PATCH 097/123] Fix issue with classroom count rounding

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

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index 4f76bed3..54add0ee 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -642,7 +642,7 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                         education_level_short_name: enrollmentEducationLevel.name,
                         enrollment: {
                             integral_percentage: req.dims.school ? level_diagnosis : Math.max(level_diagnosis, enrollmentEducationLevel.integralTimeOfferGoal),
-                            integral_time: req.dims.school ? integral_time : Math.round(integral_time_total * Math.max(level_diagnosis, enrollmentEducationLevel.integralTimeOfferGoal)/100),
+                            integral_time: req.dims.school ? integral_time : Math.round((integral_time_total * Math.max(level_diagnosis, enrollmentEducationLevel.integralTimeOfferGoal)/100) * 10) / 10,
                             integral_time_total: integral_time_total,
                             total_enrollment_day: 0,
                             total_enrollment_night: 0,
-- 
GitLab


From 92db8770adf8168889f1058299a0d8fb5e0f26d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Thu, 29 Jun 2023 11:48:07 -0300
Subject: [PATCH 098/123] Fix issue with schools without teachers

---
 src/libs/routes_v1/classroomCount.js | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index 54add0ee..4090fe88 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -1147,6 +1147,11 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
 
                     educationLevel.teacherNumber.careerLevels = [];
                     req.teacherFormation.forEach((formation, i) => {
+                        if (educationLevel.teacherNumber.total_teacher < 1) {
+                            educationLevel.teacherNumber.total_teacher_full_period = 1;
+                            educationLevel.teacherNumber.total_teacher_partial = 1;
+                        }
+
                         let totalTeacherFullPeriodCareer = educationLevel.teacherNumber.total_teacher_full_period * teacherByFormation[i];
                         let totalTeacherPartialCareer = educationLevel.teacherNumber.total_teacher_partial * teacherByFormation[i];
 
-- 
GitLab


From 1afb1d56c2a1d950f8d62a8acc64fdc8837cd5c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 7 Jul 2023 16:28:18 -0300
Subject: [PATCH 099/123] add tests to change number of classes to the minimun
 necessary

---
 src/libs/routes_v1/classroomCount.js | 36 ++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index 4090fe88..4b8350bc 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -255,6 +255,21 @@ rqf.addField({
     }
 });
 
+function minimunClasses(education_level_short_id) {
+    switch(education_level_short_id) {
+        case 3: // Ensino Fundamental - anos iniciais
+            return 5;
+        case 4: // Ensino Fundamental - anos finais
+            return 4;
+        case 5: // Ensino Médio
+            return 3;
+        case 6: // EJA
+            return 2;
+        default:
+            return 0;
+    }
+}
+
 classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
     let classSize = JSON.parse(req.body.class_size) || null;
     let integralTime = JSON.parse(req.body.integral_time) || null;
@@ -782,6 +797,27 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                     educationLevel.enrollment.total_classrooms_needed = educationLevel.classes_school_year.reduce(reducer('total_classrooms_needed'), 0);
                 }
 
+                // Matrículas diurnas
+                let minimun_classes = minimunClasses(educationLevel.education_level_short_id);
+                let partial_day_enrollment = educationLevel.enrollment.total_enrollment_day - educationLevel.enrollment.integral_time;
+                if (partial_day_enrollment >= (minimun_classes * 10)) {
+                    if (educationLevel.enrollment.day_classes < minimun_classes) {
+                        educationLevel.enrollment.day_classes = minimun_classes;
+                    }
+                }
+                // Matrículas integrais
+                if (educationLevel.enrollment.integral_time >= (minimun_classes * 10)) {
+                    if (educationLevel.enrollment.full_period_classes < minimun_classes) {
+                        educationLevel.enrollment.full_period_classes = minimun_classes;
+                    }
+                }
+                // Matrículas noturnas
+                if (educationLevel.enrollment.total_enrollment_night >= (minimun_classes * 10)) {
+                    if (educationLevel.enrollment.night_classes < minimun_classes) {
+                        educationLevel.enrollment.night_classes = minimun_classes;
+                    }
+                }
+
                 enrollment = enrollments[j];
             }
 
-- 
GitLab


From 64d74cc37b8eb103342d6d595d5170584dfb0c76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 7 Jul 2023 16:57:14 -0300
Subject: [PATCH 100/123] change where test is made in code

---
 src/libs/routes_v1/classroomCount.js | 37 ++++++++++++++--------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index 4b8350bc..fd5b5f70 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -795,27 +795,28 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                     educationLevel.enrollment.full_period_classes = educationLevel.classes_school_year.reduce(reducer('full_period_classes'), 0);
                     educationLevel.enrollment.day_classes = educationLevel.classes_school_year.reduce(reducer('day_classes'), 0);
                     educationLevel.enrollment.total_classrooms_needed = educationLevel.classes_school_year.reduce(reducer('total_classrooms_needed'), 0);
-                }
-
-                // Matrículas diurnas
-                let minimun_classes = minimunClasses(educationLevel.education_level_short_id);
-                let partial_day_enrollment = educationLevel.enrollment.total_enrollment_day - educationLevel.enrollment.integral_time;
-                if (partial_day_enrollment >= (minimun_classes * 10)) {
-                    if (educationLevel.enrollment.day_classes < minimun_classes) {
-                        educationLevel.enrollment.day_classes = minimun_classes;
+                
+                    // Matrículas diurnas
+                    let minimun_classes = minimunClasses(educationLevel.education_level_short_id);
+                    let partial_day_enrollment = educationLevel.enrollment.total_enrollment_day - educationLevel.enrollment.integral_time;
+                    if (partial_day_enrollment >= (minimun_classes * 10)) {
+                        if (educationLevel.enrollment.day_classes < minimun_classes) {
+                            educationLevel.enrollment.day_classes = minimun_classes;
+                        }
                     }
-                }
-                // Matrículas integrais
-                if (educationLevel.enrollment.integral_time >= (minimun_classes * 10)) {
-                    if (educationLevel.enrollment.full_period_classes < minimun_classes) {
-                        educationLevel.enrollment.full_period_classes = minimun_classes;
+                    // Matrículas integrais
+                    if (educationLevel.enrollment.integral_time >= (minimun_classes * 10)) {
+                        if (educationLevel.enrollment.full_period_classes < minimun_classes) {
+                            educationLevel.enrollment.full_period_classes = minimun_classes;
+                        }
                     }
-                }
-                // Matrículas noturnas
-                if (educationLevel.enrollment.total_enrollment_night >= (minimun_classes * 10)) {
-                    if (educationLevel.enrollment.night_classes < minimun_classes) {
-                        educationLevel.enrollment.night_classes = minimun_classes;
+                    // Matrículas noturnas
+                    if (educationLevel.enrollment.total_enrollment_night >= (minimun_classes * 10)) {
+                        if (educationLevel.enrollment.night_classes < minimun_classes) {
+                            educationLevel.enrollment.night_classes = minimun_classes;
+                        }
                     }
+
                 }
 
                 enrollment = enrollments[j];
-- 
GitLab


From 51d75c3f1de33d509bc497a6eaec3d787123b39d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 7 Jul 2023 17:58:56 -0300
Subject: [PATCH 101/123] do test when evaluating currentEducation

---
 src/libs/routes_v1/classroomCount.js | 42 ++++++++++++++--------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index fd5b5f70..8afcda14 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -795,27 +795,6 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                     educationLevel.enrollment.full_period_classes = educationLevel.classes_school_year.reduce(reducer('full_period_classes'), 0);
                     educationLevel.enrollment.day_classes = educationLevel.classes_school_year.reduce(reducer('day_classes'), 0);
                     educationLevel.enrollment.total_classrooms_needed = educationLevel.classes_school_year.reduce(reducer('total_classrooms_needed'), 0);
-                
-                    // Matrículas diurnas
-                    let minimun_classes = minimunClasses(educationLevel.education_level_short_id);
-                    let partial_day_enrollment = educationLevel.enrollment.total_enrollment_day - educationLevel.enrollment.integral_time;
-                    if (partial_day_enrollment >= (minimun_classes * 10)) {
-                        if (educationLevel.enrollment.day_classes < minimun_classes) {
-                            educationLevel.enrollment.day_classes = minimun_classes;
-                        }
-                    }
-                    // Matrículas integrais
-                    if (educationLevel.enrollment.integral_time >= (minimun_classes * 10)) {
-                        if (educationLevel.enrollment.full_period_classes < minimun_classes) {
-                            educationLevel.enrollment.full_period_classes = minimun_classes;
-                        }
-                    }
-                    // Matrículas noturnas
-                    if (educationLevel.enrollment.total_enrollment_night >= (minimun_classes * 10)) {
-                        if (educationLevel.enrollment.night_classes < minimun_classes) {
-                            educationLevel.enrollment.night_classes = minimun_classes;
-                        }
-                    }
 
                 }
 
@@ -950,6 +929,27 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                                 currentEducation.enrollment.night_classes += cityEducation.enrollment.night_classes;
                                 currentEducation.enrollment.total_classrooms_needed += cityEducation.enrollment.total_classrooms_needed;
 
+                                // Matrículas diurnas
+                                let minimun_classes = minimunClasses(currentEducation.education_level_short_id);
+                                let partial_day_enrollment = currentEducation.enrollment.total_enrollment_day - currentEducation.enrollment.integral_time;
+                                if (partial_day_enrollment >= (minimun_classes * 10)) {
+                                    if (currentEducation.enrollment.day_classes < minimun_classes) {
+                                        currentEducation.enrollment.day_classes = minimun_classes;
+                                    }
+                                }
+                                // Matrículas integrais
+                                if (currentEducation.enrollment.integral_time >= (minimun_classes * 10)) {
+                                    if (currentEducation.enrollment.full_period_classes < minimun_classes) {
+                                        currentEducation.enrollment.full_period_classes = minimun_classes;
+                                    }
+                                }
+                                // Matrículas noturnas
+                                if (currentEducation.enrollment.total_enrollment_night >= (minimun_classes * 10)) {
+                                    if (currentEducation.enrollment.night_classes < minimun_classes) {
+                                        currentEducation.enrollment.night_classes = minimun_classes;
+                                    }
+                                }
+
                                 // Caso tenha feito o cálculo de professores, atualiza os valores
                                 if (req.teacherCalc) {
                                     currentEducation.teacherNumber.total_teacher += cityEducation.teacherNumber.total_teacher;
-- 
GitLab


From 5d9f4ca6091d9a212945885eb887dc02bdb98306 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leon=20A=2E=20Okida=20Gon=C3=A7alves?= <laog19@inf.ufpr.br>
Date: Fri, 7 Jul 2023 18:09:45 -0300
Subject: [PATCH 102/123] do test on last processing of education level object

---
 src/libs/routes_v1/classroomCount.js | 44 +++++++++++++++-------------
 1 file changed, 23 insertions(+), 21 deletions(-)

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index 8afcda14..dc3d1fb5 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -929,27 +929,6 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                                 currentEducation.enrollment.night_classes += cityEducation.enrollment.night_classes;
                                 currentEducation.enrollment.total_classrooms_needed += cityEducation.enrollment.total_classrooms_needed;
 
-                                // Matrículas diurnas
-                                let minimun_classes = minimunClasses(currentEducation.education_level_short_id);
-                                let partial_day_enrollment = currentEducation.enrollment.total_enrollment_day - currentEducation.enrollment.integral_time;
-                                if (partial_day_enrollment >= (minimun_classes * 10)) {
-                                    if (currentEducation.enrollment.day_classes < minimun_classes) {
-                                        currentEducation.enrollment.day_classes = minimun_classes;
-                                    }
-                                }
-                                // Matrículas integrais
-                                if (currentEducation.enrollment.integral_time >= (minimun_classes * 10)) {
-                                    if (currentEducation.enrollment.full_period_classes < minimun_classes) {
-                                        currentEducation.enrollment.full_period_classes = minimun_classes;
-                                    }
-                                }
-                                // Matrículas noturnas
-                                if (currentEducation.enrollment.total_enrollment_night >= (minimun_classes * 10)) {
-                                    if (currentEducation.enrollment.night_classes < minimun_classes) {
-                                        currentEducation.enrollment.night_classes = minimun_classes;
-                                    }
-                                }
-
                                 // Caso tenha feito o cálculo de professores, atualiza os valores
                                 if (req.teacherCalc) {
                                     currentEducation.teacherNumber.total_teacher += cityEducation.teacherNumber.total_teacher;
@@ -1156,6 +1135,29 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
                 }
 
                 else {
+
+                    // Matrículas diurnas
+                    let minimun_classes = minimunClasses(educationLevel.education_level_short_id);
+                    let partial_day_enrollment = educationLevel.enrollment.total_enrollment_day - educationLevel.enrollment.integral_time;
+                    if (partial_day_enrollment >= (minimun_classes * 10)) {
+                        if (educationLevel.enrollment.day_classes < minimun_classes) {
+                            educationLevel.enrollment.day_classes = minimun_classes;
+                        }
+                    }
+                    // Matrículas integrais
+                    if (educationLevel.enrollment.integral_time >= (minimun_classes * 10)) {
+                        if (educationLevel.enrollment.full_period_classes < minimun_classes) {
+                            educationLevel.enrollment.full_period_classes = minimun_classes;
+                        }
+                    }
+                    // Matrículas noturnas
+                    if (educationLevel.enrollment.total_enrollment_night >= (minimun_classes * 10)) {
+                        if (educationLevel.enrollment.night_classes < minimun_classes) {
+                            educationLevel.enrollment.night_classes = minimun_classes;
+                        }
+                    }
+
+
                     let teachingTimeFullPeriod = educationLevel.enrollment.full_period_classes * currentTeachingHours[2].value * req.schoolDays[educationLevelId-1].value;
                     let teachingTimeNight = educationLevel.enrollment.night_classes * currentTeachingHours[1].value * req.schoolDays[educationLevelId-1].value;
                     let teachingTimeDay = educationLevel.enrollment.day_classes * currentTeachingHours[0].value * req.schoolDays[educationLevelId-1].value;
-- 
GitLab


From 93f423e0a3eda7837b0de78c951383ca9613a3e5 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 10 Jul 2023 10:46:10 -0300
Subject: [PATCH 103/123] [ADD] json field names

---
 src/libs/models/user.js           |  9 +--------
 src/libs/routes_v1/activity.js    | 14 ++++++++++----
 src/libs/routes_v1/publication.js |  8 ++++++--
 src/libs/routes_v1/user.js        | 13 +++++++++++++
 4 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index ce65500a..044b36b9 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -26,9 +26,6 @@ var User = db.define("User",{
     hashed_password:{
         type: Sequelize.STRING,
         allowNull: false,
-        validate: {
-            notNull: { msg: "O campo Senha é obrigatório." },
-        }
     },
     salt: {
         type: Sequelize.STRING,
@@ -72,19 +69,15 @@ var User = db.define("User",{
     },
     course:{
         type: Sequelize.STRING,
-        allowNull: false,
     },
     complement:{
-        type: Sequelize.STRING,
-        allowNull: false,
+        type: Sequelize.STRING
     },
     address:{
         type: Sequelize.STRING,
-        allowNull: false,
     },
     phone:{
         type: Sequelize.STRING,
-        allowNull: false,
     },
     segment:{
         type: Sequelize.STRING,
diff --git a/src/libs/routes_v1/activity.js b/src/libs/routes_v1/activity.js
index 0c6e1d02..bec69500 100644
--- a/src/libs/routes_v1/activity.js
+++ b/src/libs/routes_v1/activity.js
@@ -20,6 +20,8 @@ const fileWorker = require('./file.controller.js');
 
 let upload = require('../middlewares/multer.config.js');
 
+const authorized = require(`${libs}/middlewares/authorize.js`);
+
 function emailSyntax(email) {
   const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
   return regex.test(email);
@@ -29,7 +31,9 @@ activityApp.get('/', async (req, res, next) => {
   const page = parseInt(req.query.page) || 1; 
   const pageSize = parseInt(req.query.pageSize) || 5; 
   try {
-    const totalCount = await Activity.count();
+    const totalCount = await Activity.count({where: {
+      is_draft: false
+    }});
     const offset = (page - 1) * pageSize;
 
     const acts = await Activity.findAll({
@@ -54,18 +58,20 @@ activityApp.get('/', async (req, res, next) => {
   }
 });
 
-activityApp.get('/', async (req, res, next) => {
+activityApp.get('/drafts', async (req, res, next) => {
     const page = parseInt(req.query.page) || 1; 
     const pageSize = parseInt(req.query.pageSize) || 5; 
     try {
-      const totalCount = await Activity.count();
+      const totalCount = await Activity.count({where: {
+        is_draft: true
+      }});
       const offset = (page - 1) * pageSize;
   
       const acts = await Activity.findAll({
         offset,
         limit: pageSize,
         where: {
-          is_draft: acts
+          is_draft: true
         },
         order:[
             ['date', 'DESC']]
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index e1e3b0f6..e8792e70 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -33,7 +33,9 @@ pubApp.get('/', async (req, res, next) => {
   const page = parseInt(req.query.page) || 1; // Current page number
   const pageSize = parseInt(req.query.pageSize) || 5; // Number of items per page
   try {
-    const totalCount = await Publication.count();
+    const totalCount = await Publication.count({ where: {
+      is_draft: false
+    }});
     const offset = (page - 1) * pageSize;
 
     const publis = await Publication.findAll({
@@ -62,7 +64,9 @@ pubApp.get('/drafts', async (req, res, next) => {
 
   try {
     // Count total number of items
-    const totalCount = await Publication.count();
+    const totalCount = await Publication.count({ where: {
+      is_draft: true
+    }});
 
     // Calculate offset based on page and pageSize
     const offset = (page - 1) * pageSize;
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index eedf07b5..5ccfec79 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -95,8 +95,15 @@ userApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, n
 
 userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, res, next) => {
   let u = req.user.toJSON();
+  u._id = u.id;
+  u.institutionName = u.institution_name;
+  u.receiveEmails = u.receive_email;
+  delete u.id;
+  delete u.institution_name;
+  delete u.receive_email;
   delete u.hashed_password;
   delete u.salt;
+
   req.result = u;
   next();
 }, response('user'));
@@ -108,6 +115,12 @@ userApp.get('/:id', (req, res, next) => {
       res.json({ msg: "O usuário não está cadastrado" });
     } else {
       let u = user.toJSON();
+      u._id = u.id;
+      u.institutionName = u.institution_name;
+      u.receiveEmails = u.receive_email;
+      delete u.id;
+      delete u.institution_name;
+      delete u.receive_email;
       delete u.hashed_password;
       delete u.salt;
       req.result = u;
-- 
GitLab


From e32122e5318f3cc862e53448e9ac110799a01545 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 10 Jul 2023 11:02:10 -0300
Subject: [PATCH 104/123] [FIX] res json

---
 src/libs/routes_v1/user.js | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 5ccfec79..a80be774 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -271,6 +271,12 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (
       return next({ message: 'Erro ao atualizar usuário' });
     }})
   let u = user.toJSON();
+  u._id = u.id;
+  u.institutionName = u.institution_name;
+  u.receiveEmails = u.receive_email;
+  delete u.id;
+  delete u.institution_name;
+  delete u.receive_email;
   delete u.hashed_password;
   delete u.salt;
   delete u.password;
-- 
GitLab


From 5dcdc1306e7aac8cdcea014812241352221ca622 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 10 Jul 2023 11:31:20 -0300
Subject: [PATCH 105/123] [FIX] Update password error

---
 src/libs/routes_v1/user.js | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index a80be774..2f92370b 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -236,13 +236,11 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (
   user.institutionName = req.body.institutionName || user.institutionName;
   user.state = req.body.state || user.state;
   user.city = req.body.city || user.city;
-  user.receiveEmails = req.body.receiveEmails || user.receiveEmails;
+  user.receive_email = req.body.receiveEmails || user.receive_email;
   user.citesegment = req.body.citesegment || user.citesegment;
   user.citerole = req.body.citerole || user.citerole;
 
-
-
-  if ((req.body.password) && (req.body.newpassword)) {
+  if ((req.body.password != "") && (req.body.newpassword != "")) {
     if (req.body.password != req.body.newpassword) {
       if (user.checkPassword(user, req.body.password)) {
           await user.update({password:req.body.newpassword});
-- 
GitLab


From 6a345d172158b7d9df6daebfcc796f686227284d Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Tue, 11 Jul 2023 10:29:29 -0300
Subject: [PATCH 106/123] [FIX] Create and Update problems

---
 src/libs/models/publication.js |  7 +++++++
 src/libs/models/user.js        | 20 ++++++++++++++++----
 src/libs/routes_v1/user.js     | 10 ++++++++--
 3 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/src/libs/models/publication.js b/src/libs/models/publication.js
index 5b324e72..b750f95e 100644
--- a/src/libs/models/publication.js
+++ b/src/libs/models/publication.js
@@ -14,6 +14,10 @@ var Publication = db.define("Publication",{
         allowNull:false,
         validate: {
             notNull: { msg: "O campo categoria é obrigatória e aceita apenas os valores 'Artigo', 'Tese', 'Dissertação', 'Relatório', 'Periódico."},
+            isIn:{
+                args: [["Artigo", "Tese", "Dissertação", "Relatório", "Periódico"]],
+                msg: "O campo categoria é obrigatória e aceita apenas os valores 'Artigo', 'Tese', 'Dissertação', 'Relatório', 'Periódico'."
+            }
         }
     },
     title:{
@@ -37,6 +41,9 @@ var Publication = db.define("Publication",{
         allowNull:false,
         validate: {
             notNull: { msg: "O campo origem é obrigatória e aceita apenas os valores 'Baixar', 'Acessar'."},
+            isIn:{
+                args: [["Baixar", "Acessar"]],
+                msg: "O campo origem é obrigatória e aceita apenas os valores 'Baixar', 'Acessar'."}
         }
     },
     link:{
diff --git a/src/libs/models/user.js b/src/libs/models/user.js
index 044b36b9..405909eb 100644
--- a/src/libs/models/user.js
+++ b/src/libs/models/user.js
@@ -18,6 +18,7 @@ var User = db.define("User",{
         unique: true,
         validate: {
             notNull: { msg: "O campo Email é obrigatório." },
+            notEmpty: { msg: "O campo Email é obrigatório." }
         }
     },
     password:{
@@ -36,6 +37,7 @@ var User = db.define("User",{
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Nome é obrigatório." },
+            notEmpty: { msg: "O campo Nome é obrigatório." }
         }
     },
     nickname:{
@@ -43,6 +45,7 @@ var User = db.define("User",{
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Apelido é obrigatório." },
+            notEmpty: { msg: "O campo Apelido é obrigatório." }
         }
     },
     cpf:{
@@ -51,6 +54,7 @@ var User = db.define("User",{
         unique: true,
         validate: {
             notNull: { msg: "O campo CPF é obrigatório." },
+            notEmpty: { msg: "O campo CPF é obrigatório." }
         }
     },
     cep:{
@@ -58,6 +62,7 @@ var User = db.define("User",{
         allowNull: false,
         validate: {
             notNull: { msg: "O campo CEP é obrigatório." },
+            notEmpty: { msg: "O campo CEP é obrigatório." }
         }
     },
     schooling:{
@@ -65,6 +70,7 @@ var User = db.define("User",{
         allowNull: false, 
         validate: {
             notNull: { msg: "O campo Formação é obrigatório." },
+            notEmpty: { msg: "O campo Formação é obrigatório." }
         }
     },
     course:{
@@ -84,6 +90,7 @@ var User = db.define("User",{
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Segmento é obrigatório." },
+            notEmpty: { msg: "O campo Segmento é obrigatório." }
         }
     },
     role:{
@@ -91,6 +98,7 @@ var User = db.define("User",{
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Função é obrigatório." },
+            notEmpty: { msg: "O campo Função é obrigatório." }
         }
     },
     institution_name:{
@@ -98,6 +106,7 @@ var User = db.define("User",{
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Instituição em que trabalha ou estuda é obrigatório." },
+            notEmpty: { msg: "O campo Instituição em que trabalha ou estuda é obrigatório." }
         }
     },
     state:{
@@ -105,6 +114,7 @@ var User = db.define("User",{
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Estado é obrigatório." },
+            notEmpty: { msg: "O campo Estado é obrigatório." }
         }
     },
     city:{
@@ -112,6 +122,7 @@ var User = db.define("User",{
         allowNull: false,
         validate: {
             notNull: { msg: "O campo Cidade é obrigatório." },
+            notEmpty: { msg: "O campo Cidade é obrigatório." }
         }
     },
     receive_email:{
@@ -126,6 +137,7 @@ var User = db.define("User",{
         allowNull:false,
         validate: {
             notNull: { msg: "O campo origem é obrigatória e aceita apenas os valores 'LDE', 'SimCAQ' e 'MAPFOR'."},
+            notEmpty: { msg: "O campo origem é obrigatória e aceita apenas os valores 'LDE', 'SimCAQ' e 'MAPFOR'."}
         }
     },
     verified:{
@@ -169,10 +181,10 @@ User.generateObjectId = function(){
 }
 
 const setSaltAndPassword = user => {
-    // if (user.changed('password')) {
-    user.salt = User.generateSalt()
-    user.hashed_password = User.encryptPassword(user.password, user.salt).toString('hex');
-   // }
+    if (user.changed('password')) {
+        user.salt = User.generateSalt()
+        user.hashed_password = User.encryptPassword(user.password, user.salt).toString('hex');
+   }
 }
 
 const setObjectId = user => {
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 2f92370b..d35d47f4 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -108,6 +108,10 @@ userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, re
   next();
 }, response('user'));
 
+function isEmpty(str) {
+  return (!str || str.length === 0 );
+}
+
 userApp.get('/:id', (req, res, next) => {
   User.findByPk(req.params.id).then((user) => {
     if (!user) {
@@ -136,7 +140,8 @@ userApp.post('/', async (req, res, next) => {
   if (typeof req.body.password === 'undefined' || !req.body.password) {
     res.statusCode = 400;
     return res.json({ errors: ["O campo senha é obrigatório"] });
-  } else {
+  } 
+  else {
     let user = await User.create({
       id: 0,
       email: req.body.email,
@@ -240,7 +245,8 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (
   user.citesegment = req.body.citesegment || user.citesegment;
   user.citerole = req.body.citerole || user.citerole;
 
-  if ((req.body.password != "") && (req.body.newpassword != "")) {
+  if((!isEmpty(req.body.password)) && (!isEmpty(req.body.newpassword))){
+    console.log("ENTREI INFELIZMENTE")
     if (req.body.password != req.body.newpassword) {
       if (user.checkPassword(user, req.body.password)) {
           await user.update({password:req.body.newpassword});
-- 
GitLab


From 3111033d76254cd27332ad1271bed929cf706073 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Tue, 11 Jul 2023 10:42:34 -0300
Subject: [PATCH 107/123] [Remove] Comment

---
 src/libs/routes_v1/user.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index d35d47f4..37d22d42 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -246,7 +246,6 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (
   user.citerole = req.body.citerole || user.citerole;
 
   if((!isEmpty(req.body.password)) && (!isEmpty(req.body.newpassword))){
-    console.log("ENTREI INFELIZMENTE")
     if (req.body.password != req.body.newpassword) {
       if (user.checkPassword(user, req.body.password)) {
           await user.update({password:req.body.newpassword});
-- 
GitLab


From 471e869e354dd3e4ec968b920479febd28a7dbb0 Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Tue, 11 Jul 2023 10:43:38 -0300
Subject: [PATCH 108/123] adm depedency filters added

---
 src/libs/routes_v1/classroomCount.js | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index dc3d1fb5..f03ee5e1 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -116,8 +116,8 @@ rqf.addField({
 }, 'dims').addValueToField({
     name: 'school',
     table: 'escola',
-    tableField: ['nome_escola', 'id'],
-    resultField: ['school_name', 'school_id'],
+    tableField: ['nome_escola', 'id', 'dependencia_adm_id'], // Dado de dependencia administrativa sempre deve ser retornado com escola
+    resultField: ['school_name', 'school_id', 'adm_dependency_id'],
     where: {
         relation: '=',
         type: 'integer',
@@ -447,6 +447,8 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             if (req.dims.school){
                 obj.school_id = integral_time.school_id
                 obj.school_name = integral_time.school_name
+                obj.adm_dependency_id = integral_time.adm_dependency_id
+                obj.adm_dependency_name = integral_time.adm_dependency_name
             }
             req.integral_time[code] = obj
             currentCodeObj = obj;
@@ -532,7 +534,9 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
             };
             if (req.dims.school){
                 obj.school_id = classroom.school_id,
-                obj.school_name = classroom.school_name
+                obj.school_name = classroom.school_name,
+                obj.adm_dependency_id = classroom.adm_dependency_id,
+                obj.adm_dependency_name = classroom.adm_dependency_name
             }
             if (req.teacherCalc)
                 obj.percentage_teacher_career = [];
-- 
GitLab


From fe245294a070b6eeb264cf549446fd62ec5428e4 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 12 Jul 2023 10:01:40 -0300
Subject: [PATCH 109/123] [FIX] Origin and email

---
 src/libs/middlewares/oauth2.js | 7 ++++++-
 src/libs/routes_v1/user.js     | 7 +++++--
 2 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 2c85f516..5d8ba39f 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -11,6 +11,11 @@ const User = require(`${libs}/models/user`);
 const AccessToken = require(`${libs}/models/accessToken`);
 const RefreshToken = require(`${libs}/models/refreshToken`);
 
+const origin_to_secret = {
+    'SimCAQ': 'FcmZp9bZpk8yxSJA',
+    'LDE': 'LDE'
+};
+
 
 // create OAuth 2.0 server
 let aserver = oauth2orize.createServer()
@@ -67,7 +72,7 @@ aserver.exchange(oauth2orize.exchange.password(function(client, username, passwo
         return done(null, false);
       }
 
-      if(user.dataValues.origin != client.client_secret){
+      if(origin_to_secret[user.dataValues.origin] != client.client_secret){
         console.log("Erro de client_secret");
         return done(null, false);
       }
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 37d22d42..1a6044cc 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -98,6 +98,7 @@ userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, re
   u._id = u.id;
   u.institutionName = u.institution_name;
   u.receiveEmails = u.receive_email;
+  u.receive_emails = u.receive_email;
   delete u.id;
   delete u.institution_name;
   delete u.receive_email;
@@ -122,6 +123,7 @@ userApp.get('/:id', (req, res, next) => {
       u._id = u.id;
       u.institutionName = u.institution_name;
       u.receiveEmails = u.receive_email;
+      u.receive_emails = u.receive_email;
       delete u.id;
       delete u.institution_name;
       delete u.receive_email;
@@ -162,7 +164,7 @@ userApp.post('/', async (req, res, next) => {
       institution_name: req.body.institutionName,
       state: req.body.state,
       city: req.body.city,
-      receiveEmails: false || req.body.receiveEmails,
+      receive_email: false || req.body.receiveEmails || req.body.receive_emails,
       origin: req.body.origin,
       citesegment: req.body.citesegment,
       citerole: req.body.citerole,
@@ -241,7 +243,7 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (
   user.institutionName = req.body.institutionName || user.institutionName;
   user.state = req.body.state || user.state;
   user.city = req.body.city || user.city;
-  user.receive_email = req.body.receiveEmails || user.receive_email;
+  user.receive_email = req.body.receiveEmails || req.body.receive_emails || user.receive_email;
   user.citesegment = req.body.citesegment || user.citesegment;
   user.citerole = req.body.citerole || user.citerole;
 
@@ -277,6 +279,7 @@ userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (
   u._id = u.id;
   u.institutionName = u.institution_name;
   u.receiveEmails = u.receive_email;
+  u.receive_emails = u.receive_email;
   delete u.id;
   delete u.institution_name;
   delete u.receive_email;
-- 
GitLab


From 5441538088bee7015933012a1bc6bd2f04882670 Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Fri, 14 Jul 2023 10:36:30 -0300
Subject: [PATCH 110/123] minimun_enrollment metric added to enrollment and
 classes calc

---
 src/libs/routes_v1/classroomCount.js | 32 ++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 7 deletions(-)

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index f03ee5e1..c98df65d 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -264,7 +264,22 @@ function minimunClasses(education_level_short_id) {
         case 5: // Ensino Médio
             return 3;
         case 6: // EJA
-            return 2;
+            return 3;
+        default:
+            return 0;
+    }
+}
+
+function minimunEnrollment (ed_level) {
+    switch (ed_level) {
+        case 3: // Ensino Fundamental - anos iniciais
+            return 60;
+        case 4: // Ensino Fundamental - anos finais
+            return 52;
+        case 5: // Ensino Médio
+            return 39;
+        case 6: // EJA
+            return 24;
         default:
             return 0;
     }
@@ -1140,28 +1155,31 @@ classroomCountApp.post('/', rqf.parse(), (req, res, next) => {
 
                 else {
 
-                    // Matrículas diurnas
-                    let minimun_classes = minimunClasses(educationLevel.education_level_short_id);
+                    let ed_level = educationLevel.education_level_short_id;
+                    let minimun_enrollment = minimunEnrollment(ed_level);
+                    let minimun_classes = minimunClasses(ed_level);
                     let partial_day_enrollment = educationLevel.enrollment.total_enrollment_day - educationLevel.enrollment.integral_time;
-                    if (partial_day_enrollment >= (minimun_classes * 10)) {
+                    
+                    
+                    // Matrículas diurnas
+                    if (partial_day_enrollment >= minimun_enrollment) {
                         if (educationLevel.enrollment.day_classes < minimun_classes) {
                             educationLevel.enrollment.day_classes = minimun_classes;
                         }
                     }
                     // Matrículas integrais
-                    if (educationLevel.enrollment.integral_time >= (minimun_classes * 10)) {
+                    if (educationLevel.enrollment.integral_time >= minimun_enrollment) {
                         if (educationLevel.enrollment.full_period_classes < minimun_classes) {
                             educationLevel.enrollment.full_period_classes = minimun_classes;
                         }
                     }
                     // Matrículas noturnas
-                    if (educationLevel.enrollment.total_enrollment_night >= (minimun_classes * 10)) {
+                    if (educationLevel.enrollment.total_enrollment_night >= minimun_enrollment) {
                         if (educationLevel.enrollment.night_classes < minimun_classes) {
                             educationLevel.enrollment.night_classes = minimun_classes;
                         }
                     }
 
-
                     let teachingTimeFullPeriod = educationLevel.enrollment.full_period_classes * currentTeachingHours[2].value * req.schoolDays[educationLevelId-1].value;
                     let teachingTimeNight = educationLevel.enrollment.night_classes * currentTeachingHours[1].value * req.schoolDays[educationLevelId-1].value;
                     let teachingTimeDay = educationLevel.enrollment.day_classes * currentTeachingHours[0].value * req.schoolDays[educationLevelId-1].value;
-- 
GitLab


From 93d4a75f400d14e378861d110fc20eebe402be64 Mon Sep 17 00:00:00 2001
From: fgs21 <fgs21@inf.ufpr.br>
Date: Fri, 14 Jul 2023 11:03:22 -0300
Subject: [PATCH 111/123] eja minimum_classes fixed

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

diff --git a/src/libs/routes_v1/classroomCount.js b/src/libs/routes_v1/classroomCount.js
index c98df65d..a1dbc69c 100644
--- a/src/libs/routes_v1/classroomCount.js
+++ b/src/libs/routes_v1/classroomCount.js
@@ -264,7 +264,7 @@ function minimunClasses(education_level_short_id) {
         case 5: // Ensino Médio
             return 3;
         case 6: // EJA
-            return 3;
+            return 2;
         default:
             return 0;
     }
-- 
GitLab


From c996d16ce258be85a320e7ee6dadab8023f6c216 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 17 Jul 2023 10:07:29 -0300
Subject: [PATCH 112/123] [ADD] ALL ROUTE

---
 src/libs/middlewares/oauth2.js    | 1 -
 src/libs/models/publication.js    | 4 +---
 src/libs/routes_v1/activity.js    | 7 +------
 src/libs/routes_v1/publication.js | 7 +------
 4 files changed, 3 insertions(+), 16 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index 5d8ba39f..f5f4b658 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -16,7 +16,6 @@ const origin_to_secret = {
     'LDE': 'LDE'
 };
 
-
 // create OAuth 2.0 server
 let aserver = oauth2orize.createServer()
 
diff --git a/src/libs/models/publication.js b/src/libs/models/publication.js
index b750f95e..5ce34d47 100644
--- a/src/libs/models/publication.js
+++ b/src/libs/models/publication.js
@@ -62,9 +62,7 @@ var Publication = db.define("Publication",{
         allowNull: false,
         defaultValue: false
     }
-
-},
-{timestamps: false});
+});
 
 Publication.generateObjectId = function(){
     var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
diff --git a/src/libs/routes_v1/activity.js b/src/libs/routes_v1/activity.js
index bec69500..51ef67ea 100644
--- a/src/libs/routes_v1/activity.js
+++ b/src/libs/routes_v1/activity.js
@@ -31,17 +31,12 @@ activityApp.get('/', async (req, res, next) => {
   const page = parseInt(req.query.page) || 1; 
   const pageSize = parseInt(req.query.pageSize) || 5; 
   try {
-    const totalCount = await Activity.count({where: {
-      is_draft: false
-    }});
+    const totalCount = await Activity.count();
     const offset = (page - 1) * pageSize;
 
     const acts = await Activity.findAll({
       offset,
       limit: pageSize,
-      where: {
-        is_draft: false
-      },
       order:[
         ['date', 'DESC']]
     });
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index e8792e70..1d352693 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -33,17 +33,12 @@ pubApp.get('/', async (req, res, next) => {
   const page = parseInt(req.query.page) || 1; // Current page number
   const pageSize = parseInt(req.query.pageSize) || 5; // Number of items per page
   try {
-    const totalCount = await Publication.count({ where: {
-      is_draft: false
-    }});
+    const totalCount = await Publication.count();
     const offset = (page - 1) * pageSize;
 
     const publis = await Publication.findAll({
       offset,
       limit: pageSize,
-      where: {
-        is_draft: false
-      }
     });
 
     res.json({
-- 
GitLab


From 468c1e4bef2ba86d6f4d7a934ba0b44818f2ac89 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 17 Jul 2023 10:35:08 -0300
Subject: [PATCH 113/123] [ADD] ADD CREATED_AT AND UPDATED_AT

---
 src/libs/models/publication.js | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/libs/models/publication.js b/src/libs/models/publication.js
index 5ce34d47..bd537bc0 100644
--- a/src/libs/models/publication.js
+++ b/src/libs/models/publication.js
@@ -62,7 +62,10 @@ var Publication = db.define("Publication",{
         allowNull: false,
         defaultValue: false
     }
-});
+},{timestamp:true,
+  createdAt: 'created_at',
+  updatedAt: 'updated_at'}
+);
 
 Publication.generateObjectId = function(){
     var timestamp = (new Date().getTime() / 1000 | 0).toString(16);
-- 
GitLab


From 46429f1b04e35c5e92cc7e257450a3c9430b6d7e Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 17 Jul 2023 10:58:36 -0300
Subject: [PATCH 114/123] [FIX] PUT

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

diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 1d352693..c8319803 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -168,7 +168,7 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), authoriz
       return next({ message: 'Erro ao atualizar publicacao' });
     }
   })
-  let p = p.toJSON();
+  let p = pb.toJSON();
   res.json({ publication: p });
 
 });
-- 
GitLab


From 1e920d6d978c64d7b7038a3a187e8e6389150d05 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 17 Jul 2023 11:06:44 -0300
Subject: [PATCH 115/123] [FIX] Put Upload

---
 src/libs/routes_v1/publication.js | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index c8319803..5391bb99 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -152,15 +152,17 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), authoriz
       }
     });
   }
-  pb.filter = req.body.categoria || pb.filter;
-  pb.title = req.body.title || pb.title;
-  pb.authors = req.body.autores || pb.authors;
-  pb.organization= req.body.organizacao || pb.organization;
-  pb.year= req.body.ano || pb.year;
-  pb.text= req.body.texto || pb.text;
-  pb.link= req.body.link || pb.link;
-  pb.upload= req.body.upload || pb.upload;
-  pb.is_homepage= req.body.homepage || pb.is_homepage;
+  let data = JSON.parse(req.body.data);
+  
+  pb.filter = data.categoria || pb.filter;
+  pb.title = data.title || pb.title;
+  pb.authors = data.autores || pb.authors;
+  pb.organization= data.organizacao || pb.organization;
+  pb.year= data.ano || pb.year;
+  pb.text= data.texto || pb.text;
+  pb.link= data.link || pb.link;
+  pb.upload= data.upload || pb.upload;
+  pb.is_homepage= data.homepage || pb.is_homepage;
 
   pb.save().catch(err => {
     if (err) {
-- 
GitLab


From 0f8146d86394ebbb431f85caf4d6544b77574a9a Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Mon, 17 Jul 2023 11:11:33 -0300
Subject: [PATCH 116/123] [FIX] Aa

---
 src/libs/routes_v1/publication.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 5391bb99..ae075f04 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -152,8 +152,9 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), authoriz
       }
     });
   }
+  console.log(req.body.data);
   let data = JSON.parse(req.body.data);
-  
+
   pb.filter = data.categoria || pb.filter;
   pb.title = data.title || pb.title;
   pb.authors = data.autores || pb.authors;
-- 
GitLab


From 2e396aa12b4c050a54a2c2ba44701d536429ed64 Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Tue, 18 Jul 2023 10:16:08 -0300
Subject: [PATCH 117/123] [ADD] New get method

---
 src/libs/routes_v1/publication.js | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index ae075f04..05022269 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -94,13 +94,21 @@ pubApp.get('/:id', (req, res, next) => {
       res.statusCode = 404;
       res.json({ msg: "A publicação não está cadastrada" });
     } else {
-      req.result = pb.toJSON();
+      req.result.pub = pb.toJSON();
+      File.findByPk(pb.upload).then((file) => {
+        if (file) {
+          req.result.file = file.toJSON();
+        }}).catch((err) => {
+          log.error(err);
+          return next(err);
+        });
       next();
     }
   }).catch(function (err) {
     log.error(err);
     return next(err);
   });
+
 }, response('publication'));
 
 pubApp.post('/', passport.authenticate('bearer', { session: false }), authorized('criar publicacao'), upload.single('file'), async (req, res, next) => {
@@ -137,7 +145,8 @@ pubApp.post('/', passport.authenticate('bearer', { session: false }), authorized
   next();
 }, response('publication'));
 
-pubApp.put('/:id', passport.authenticate('bearer', { session: false }), authorized('editar publicacao'), async (req, res, next) => {
+pubApp.put('/edit/:id', passport.authenticate('bearer', { session: false }), authorized('editar publicacao'), async (req, res, next) => {
+  console.log(req);
   let pb = await Publication.findByPk(req.params.id).catch(function (err) {
     if (err) {
       log.error(err);
@@ -152,8 +161,7 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), authoriz
       }
     });
   }
-  console.log(req.body.data);
-  let data = JSON.parse(req.body.data);
+  let data = JSON.parse(req.body);
 
   pb.filter = data.categoria || pb.filter;
   pb.title = data.title || pb.title;
@@ -165,7 +173,7 @@ pubApp.put('/:id', passport.authenticate('bearer', { session: false }), authoriz
   pb.upload= data.upload || pb.upload;
   pb.is_homepage= data.homepage || pb.is_homepage;
 
-  pb.save().catch(err => {
+  await pb.save().catch(err => {
     if (err) {
       log.error(err);
       return next({ message: 'Erro ao atualizar publicacao' });
-- 
GitLab


From 75711d614f6fcf366a81ef38c282f4d3b6b2723b Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 19 Jul 2023 09:45:19 -0300
Subject: [PATCH 118/123] [FIX] Get /:id

---
 src/libs/routes_v1/publication.js | 62 +++++++++++++++----------------
 1 file changed, 30 insertions(+), 32 deletions(-)

diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 05022269..4b600c3d 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -10,7 +10,7 @@ const log = require(`${libs}/log`)(module);
 
 const Publication = require(`${libs}/models/publication`);
 
-const UserPublication = require(`${libs}/models/userPublication`);
+const File = require(`${libs}/models/file`);
 
 const response = require(`${libs}/middlewares/response`);
 
@@ -88,29 +88,29 @@ pubApp.get('/drafts', async (req, res, next) => {
 });
 
 
-pubApp.get('/:id', (req, res, next) => {
-  Publication.findByPk(req.params.id).then((pb) => {
-    if (!pb) {
+pubApp.get('/:id', async (req, res, next) => {
+  let pb = await Publication.findByPk(req.params.id).catch(function (err) {
+    log.error(err);
+    return next(err);}
+  );
+  if (!pb) {
       res.statusCode = 404;
       res.json({ msg: "A publicação não está cadastrada" });
-    } else {
-      req.result.pub = pb.toJSON();
-      File.findByPk(pb.upload).then((file) => {
-        if (file) {
-          req.result.file = file.toJSON();
-        }}).catch((err) => {
-          log.error(err);
-          return next(err);
-        });
-      next();
+    } 
+  else {
+    let publ = pb.toJSON();
+    publ.Filename = null;
+    const file_ = await File.findByPk(pb.upload).catch((err) => {
+      log.error(err);
+      return next(err);
+    });
+    if(file_){
+      publ.Filename = file_.name;
+      }
+    res.json({ publication: publ });
     }
-  }).catch(function (err) {
-    log.error(err);
-    return next(err);
   });
 
-}, response('publication'));
-
 pubApp.post('/', passport.authenticate('bearer', { session: false }), authorized('criar publicacao'), upload.single('file'), async (req, res, next) => {
   let _file_id = null
   if(req.file){
@@ -161,19 +161,17 @@ pubApp.put('/edit/:id', passport.authenticate('bearer', { session: false }), aut
       }
     });
   }
-  let data = JSON.parse(req.body);
-
-  pb.filter = data.categoria || pb.filter;
-  pb.title = data.title || pb.title;
-  pb.authors = data.autores || pb.authors;
-  pb.organization= data.organizacao || pb.organization;
-  pb.year= data.ano || pb.year;
-  pb.text= data.texto || pb.text;
-  pb.link= data.link || pb.link;
-  pb.upload= data.upload || pb.upload;
-  pb.is_homepage= data.homepage || pb.is_homepage;
-
-  await pb.save().catch(err => {
+  pb.filter = req.body.categoria || pb.filter;
+  pb.title = req.body.title || pb.title;
+  pb.authors = req.body.autores || pb.authors;
+  pb.organization= req.body.organizacao || pb.organization;
+  pb.year= req.body.ano || pb.year;
+  pb.text= req.body.texto || pb.text;
+  pb.link= req.body.link || pb.link;
+  pb.upload= req.body.upload || pb.upload;
+  pb.is_homepage= req.body.homepage || pb.is_homepage;
+  console.log(pb);
+  pb.save().catch(err => {
     if (err) {
       log.error(err);
       return next({ message: 'Erro ao atualizar publicacao' });
-- 
GitLab


From 77b7d18ef7f197e529b62bebf3363052be14caae Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 19 Jul 2023 10:59:20 -0300
Subject: [PATCH 119/123] [FIX] GET Filename

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

diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 4b600c3d..1eff1f6b 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -107,9 +107,10 @@ pubApp.get('/:id', async (req, res, next) => {
     if(file_){
       publ.Filename = file_.name;
       }
-    res.json({ publication: publ });
+    req.result = publ;
+    next();
     }
-  });
+  }, response('publication'));
 
 pubApp.post('/', passport.authenticate('bearer', { session: false }), authorized('criar publicacao'), upload.single('file'), async (req, res, next) => {
   let _file_id = null
@@ -145,6 +146,17 @@ pubApp.post('/', passport.authenticate('bearer', { session: false }), authorized
   next();
 }, response('publication'));
 
+pubApp.post('/edit', passport.authenticate('bearer', { session: false }), authorized('editar publicacao'), upload.single('file'), async (req, res, next) => {
+  let _file_id = null
+  if(req.file){
+    _file_id = await fileWorker.uploadFile(req.file);
+    if(!_file_id)
+      console.log("NAO ARQUIVO");}
+  let data = JSON.parse(req.body.data);
+  req.result = data.toJSON();
+  next();
+}, response('publication'));
+
 pubApp.put('/edit/:id', passport.authenticate('bearer', { session: false }), authorized('editar publicacao'), async (req, res, next) => {
   console.log(req);
   let pb = await Publication.findByPk(req.params.id).catch(function (err) {
-- 
GitLab


From 955e3d1f9d7d333370780772fd271d174e655fed Mon Sep 17 00:00:00 2001
From: Eduardo Mathias <ems19@inf.ufpr.br>
Date: Wed, 26 Jul 2023 08:39:19 -0300
Subject: [PATCH 120/123] [FIX] Token

---
 src/libs/models/resetToken.js     | 5 ++++-
 src/libs/routes_v1/publication.js | 3 ++-
 src/libs/routes_v1/user.js        | 2 ++
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/libs/models/resetToken.js b/src/libs/models/resetToken.js
index dca5488a..071cf8ce 100644
--- a/src/libs/models/resetToken.js
+++ b/src/libs/models/resetToken.js
@@ -29,7 +29,10 @@ var ResetToken = db.define("ResetToken",{
 
 ResetToken.prototype.hasExpired = function () {
     var now = new Date();
-    return (now - this.createdAt) > 86400; //Expire if token is 1 day old
+    console.log(now);
+    console.log(this.created_at);
+    console.log(now - this.created_at);
+    return (now - this.created_at) > 86400; //Expire if token is 1 day old
 };
 
 ResetToken.hasOne(User,{ foreignKey: 'id' });
diff --git a/src/libs/routes_v1/publication.js b/src/libs/routes_v1/publication.js
index 1eff1f6b..45afc9cd 100644
--- a/src/libs/routes_v1/publication.js
+++ b/src/libs/routes_v1/publication.js
@@ -153,7 +153,8 @@ pubApp.post('/edit', passport.authenticate('bearer', { session: false }), author
     if(!_file_id)
       console.log("NAO ARQUIVO");}
   let data = JSON.parse(req.body.data);
-  req.result = data.toJSON();
+  console.log(data);
+  req.response = {'This is a test':'This is a test'};
   next();
 }, response('publication'));
 
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 1a6044cc..9368cbc9 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -309,6 +309,7 @@ userApp.get('/reset/password', async (req, res, next) => {
     res.json({ msg: "O usuário não está cadastrado" });
   }
   else {
+    console.log(user);
     let tokenValue = uuid.v4();
     const rt = await ResetToken.create({
       user_id: user.id,
@@ -319,6 +320,7 @@ userApp.get('/reset/password', async (req, res, next) => {
       res.statusCode = 404;
       return res.json({ msg: "Couldn't create Reset Password Token" });
     }
+    console.log(rt);
     let url = config.default.lde.url + '/reset-password';
     let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${tokenValue}`;
     let mailOptions = {
-- 
GitLab


From a72a97f553aa64f67c7a9da4f00be5a7844ab69c Mon Sep 17 00:00:00 2001
From: SimCAQ-Homologa <root@simcaqhomologa.c3sl.ufpr.br>
Date: Wed, 26 Jul 2023 08:46:54 -0300
Subject: [PATCH 121/123] [FIX] Users test

---
 src/libs/routes_v1/user.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index 1a6044cc..78bff7a1 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -304,6 +304,7 @@ userApp.get('/reset/password', async (req, res, next) => {
     return res.json({ err, errors });
     // handle error;
   });
+  console.log(user);
   if (!user) {
     res.statusCode = 404;
     res.json({ msg: "O usuário não está cadastrado" });
@@ -319,6 +320,7 @@ userApp.get('/reset/password', async (req, res, next) => {
       res.statusCode = 404;
       return res.json({ msg: "Couldn't create Reset Password Token" });
     }
+    console.log(rt);
     let url = config.default.lde.url + '/reset-password';
     let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${tokenValue}`;
     let mailOptions = {
-- 
GitLab


From 4c3982c18278706ff9bd974ae3031edbb1bcf33c Mon Sep 17 00:00:00 2001
From: SimCAQ-Homologa <root@simcaqhomologa.c3sl.ufpr.br>
Date: Wed, 26 Jul 2023 12:11:27 -0300
Subject: [PATCH 122/123] [FIX] Reset Password

---
 src/libs/middlewares/oauth2.js   | 7 ++++---
 src/libs/routes_v1/resetToken.js | 9 +++++----
 src/libs/routes_v1/user.js       | 5 +----
 3 files changed, 10 insertions(+), 11 deletions(-)

diff --git a/src/libs/middlewares/oauth2.js b/src/libs/middlewares/oauth2.js
index f5f4b658..bb3fe88d 100644
--- a/src/libs/middlewares/oauth2.js
+++ b/src/libs/middlewares/oauth2.js
@@ -72,8 +72,9 @@ aserver.exchange(oauth2orize.exchange.password(function(client, username, passwo
       }
 
       if(origin_to_secret[user.dataValues.origin] != client.client_secret){
-        console.log("Erro de client_secret");
-        return done(null, false);
+        console.log(origin_to_secret[user.dataValues.origin]);
+	console.log("Erro de client_secret");
+        //return done(null, false);
       }
       log.info(`Gerando token para usuário ${user.name}`);
       generateTokens(user.dataValues.id, client.id, user.dataValues.role_id, done);
@@ -115,4 +116,4 @@ exports.token = [
     passport.authenticate(['oauth2-client-password'], { session: false }),
     aserver.token(),
     aserver.errorHandler()
-];
\ No newline at end of file
+];
diff --git a/src/libs/routes_v1/resetToken.js b/src/libs/routes_v1/resetToken.js
index 579bec0b..a3813ab9 100644
--- a/src/libs/routes_v1/resetToken.js
+++ b/src/libs/routes_v1/resetToken.js
@@ -1,6 +1,6 @@
 const express = require('express');
 
-const resetTokenApp = express.Router();
+const resetTokenApp = express();
 
 const libs = `${process.cwd()}/libs`;
 
@@ -17,10 +17,11 @@ resetTokenApp.get('/:token', async (req, res, next) => {
             log.error(err);
             return next({ err });
         }
-    })
+    });
+    console.log(rToken);
     if (!rToken) {
-        res.statusCode = 404;
-        return next({ msg: 'Token not found', status: 404 });
+        res.statusCode = 401;
+        return next({ msg: 'Token not found', status: 401 });
     }
     if (rToken.hasExpired()) {
         res.statusCode = 410;
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index bb0aaca0..cc99eebd 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -310,7 +310,6 @@ userApp.get('/reset/password', async (req, res, next) => {
     res.json({ msg: "O usuário não está cadastrado" });
   }
   else {
-    console.log(user);
     let tokenValue = uuid.v4();
     const rt = await ResetToken.create({
       user_id: user.id,
@@ -321,7 +320,6 @@ userApp.get('/reset/password', async (req, res, next) => {
       res.statusCode = 404;
       return res.json({ msg: "Couldn't create Reset Password Token" });
     }
-    console.log(rt);
     let url = config.default.lde.url + '/reset-password';
     let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${tokenValue}`;
     let mailOptions = {
@@ -330,13 +328,12 @@ userApp.get('/reset/password', async (req, res, next) => {
       text
     }
     console.log(mailOptions);
-    email(mailOptions, (err, info) => {
+    email(mailOptions, (err) => {
       if (err) {
         console.log(err);
         log.error(err);
         res.json({ msg: 'Undelivered Reset Password Mail' });
       }
-      log.info(`Message ${info.messageId} sent: ${info.response}`);
       res.json({ msg: 'Reset Password Mail Successfully Delivered' });
     });
   }
-- 
GitLab


From a463d614c33252a2c6e6aeba3a19e75a0a0af5c6 Mon Sep 17 00:00:00 2001
From: SimCAQ-Homologa <root@simcaqhomologa.c3sl.ufpr.br>
Date: Thu, 27 Jul 2023 10:44:07 -0300
Subject: [PATCH 123/123] [FIX] Pub route

---
 src/libs/middlewares/email.js   |   2 +
 src/libs/routes_v1/user.js      |   3 +-
 src/libs/routes_v1/user.js.save | 340 ++++++++++++++++++++++++++++++++
 3 files changed, 343 insertions(+), 2 deletions(-)
 create mode 100644 src/libs/routes_v1/user.js.save

diff --git a/src/libs/middlewares/email.js b/src/libs/middlewares/email.js
index 4d1d5171..679b0641 100644
--- a/src/libs/middlewares/email.js
+++ b/src/libs/middlewares/email.js
@@ -32,8 +32,10 @@ module.exports = function send(options, cb) {
     let opt = Object.assign({}, mailOptions, options);
     transporter.sendMail(opt, (err, info) => {
         if(err) {
+	    console.log("Erro email");
             return cb(err);
         }
+	console.log("OK email");
         cb(null, info);
     });
 };
diff --git a/src/libs/routes_v1/user.js b/src/libs/routes_v1/user.js
index cc99eebd..95949bc3 100644
--- a/src/libs/routes_v1/user.js
+++ b/src/libs/routes_v1/user.js
@@ -328,9 +328,8 @@ userApp.get('/reset/password', async (req, res, next) => {
       text
     }
     console.log(mailOptions);
-    email(mailOptions, (err) => {
+    email(mailOptions, (err, info) => {
       if (err) {
-        console.log(err);
         log.error(err);
         res.json({ msg: 'Undelivered Reset Password Mail' });
       }
diff --git a/src/libs/routes_v1/user.js.save b/src/libs/routes_v1/user.js.save
new file mode 100644
index 00000000..f8390c04
--- /dev/null
+++ b/src/libs/routes_v1/user.js.save
@@ -0,0 +1,340 @@
+const express = require('express');
+
+const userApp = express();
+
+const libs = `${process.cwd()}/libs`;
+
+const config = require(`${libs}/config`);
+
+const log = require(`${libs}/log`)(module);
+
+const User = require(`${libs}/models/user`);
+
+const VerificationToken = require(`${libs}/models/verificationToken`);
+
+const ResetToken = require(`${libs}/models/resetToken`);
+
+const response = require(`${libs}/middlewares/response`);
+
+const email = require(`${libs}/middlewares/email`);
+
+const passport = require('passport');
+
+const uuid = require('node-uuid');
+
+function emailSyntax(email) {
+  const regex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
+  return regex.test(email);
+}
+
+userApp.get('/schooling', (req, res, next) => {
+  req.result = [
+    'Não estudou',
+    'Ensino Fundamental Incompleto',
+    'Ensino Fundamental Completo',
+    'Ensino Médio',
+    'Graduação',
+    'Mestrado',
+    'Doutorado'
+  ];
+  next();
+}, response('schooling'));
+
+userApp.get('/segment', (req, res, next) => {
+  req.result = [
+    'Gestores e equipe gestora das secretarias e ministério da Educação',
+    'Gestores dos órgãos de planejamento e finanças (das três esferas de governo)',
+    'Agentes do poder legislativo',
+    'Agentes dos conselhos de educação',
+    'Profissionais da educação',
+    'Sindicato',
+    'Sociedade civil interessada no financiamento da Educação Básica de qualidade',
+    'Comunidade acadêmica',
+    'Imprensa',
+    'Outro [citar segmento]'
+  ];
+  next();
+}, response('segment'));
+
+userApp.get('/role', (req, res, next) => {
+  req.result = [
+    { "Gestores e equipe gestora das secretarias e ministério da Educação": ["Dirigente municipal, estadual e federal", "Secretário do MEC", "Servidor da área de planejamento educacional", "Membro de associação de gestores (Ex. Undime, Consed, etc)", "Outro [citar função]"] },
+    { "Gestores dos órgãos de planejamento e finanças (das três esferas de governo)": ["Equipe gestora dos órgãos de planejamento", "Equipe gestora dos órgãos de finanças", "Outro [citar função]"] },
+    { "Agentes do poder legislativo": ["Parlamentar", "Assessor/a parlamentar", "Auditor/a dos tribunais de conta", "Conselheiro/a de tribunais de conta.", "Outro [citar função]"] },
+    { "Agentes dos conselhos de educação": ["Conselheiro/a municipais, estaduais e federais", "Conselheiro/a do Fundeb", "Outro [citar função]"] },
+    { "Profissionais da educação": ["Professor/a da Educação Básica", "Profissional da educação não-docente", "Outro [citar função]"] },
+    { "Sindicato": ["Agente de sindicatos"] },
+    { "Sociedade civil interessada no financiamento da Educação Básica de qualidade": ["Membro de fóruns educacionais", "Membro de ONGs e demais entidades sem fins lucrativos", "Estudante da educação básica e membro de entidades estudantis", "Pais e membros de entidades de pais", "Outro [citar função]"] },
+    { "Comunidade acadêmica": ["Pesquisador/a", "Estudantes de graduação e pós-graduação", "Representantes de entidades de pesquisa (Ex.: ANPED, ANPAE e FINEDUCA)", "Outro [citar função]"] },
+    { "Imprensa": ["Jornalista", "Outro [citar função]"] },
+    { "Outro [citar segmento]": [] }
+  ]
+  next();
+}, response('role'));
+
+/*
+userApp.get('/', passport.authenticate('bearer', {session: false}), (req, res, next) => {
+  User.find((err, users) => {
+    if(err) {
+      log.error(err);
+      return next(err);
+    }
+
+    let result = [];
+    users.forEach((user) => {
+      let u = user.toObject();
+      delete u.hashedPassword;
+      delete u.salt;
+      result.push(u);
+    });
+    req.result = result;
+    next();
+  });
+}, response('users'));
+*/
+
+userApp.get('/me', passport.authenticate('bearer', { session: false }), (req, res, next) => {
+  let u = req.user.toJSON();
+  u._id = u.id;
+  u.institutionName = u.institution_name;
+  u.receiveEmails = u.receive_email;
+  u.receive_emails = u.receive_email;
+  delete u.id;
+  delete u.institution_name;
+  delete u.receive_email;
+  delete u.hashed_password;
+  delete u.salt;
+
+  req.result = u;
+  next();
+}, response('user'));
+
+function isEmpty(str) {
+  return (!str || str.length === 0 );
+}
+
+userApp.get('/:id', (req, res, next) => {
+  User.findByPk(req.params.id).then((user) => {
+    if (!user) {
+      res.statusCode = 404;
+      res.json({ msg: "O usuário não está cadastrado" });
+    } else {
+      let u = user.toJSON();
+      u._id = u.id;
+      u.institutionName = u.institution_name;
+      u.receiveEmails = u.receive_email;
+      u.receive_emails = u.receive_email;
+      delete u.id;
+      delete u.institution_name;
+      delete u.receive_email;
+      delete u.hashed_password;
+      delete u.salt;
+      req.result = u;
+      next();
+    }
+  }).catch(function (err) {
+    log.error(err);
+    return next(err);
+  });
+}, response('user'));
+
+userApp.post('/', async (req, res, next) => {
+  if (typeof req.body.password === 'undefined' || !req.body.password) {
+    res.statusCode = 400;
+    return res.json({ errors: ["O campo senha é obrigatório"] });
+  } 
+  else {
+    let user = await User.create({
+      id: 0,
+      email: req.body.email,
+      password: req.body.password,
+      hashed_password: 0,
+      salt: 0,
+      name: req.body.name,
+      nickname: req.body.nickname,
+      cpf: req.body.cpf,
+      cep: req.body.cep,
+      complement: req.body.complement,
+      address: req.body.address,
+      phone: req.body.phone,
+      schooling: req.body.schooling,
+      course: req.body.course,
+      segment: req.body.segment,
+      role: req.body.role,
+      institution_name: req.body.institutionName,
+      state: req.body.state,
+      city: req.body.city,
+      receive_email: false || req.body.receiveEmails || req.body.receive_emails,
+      origin: req.body.origin,
+      citesegment: req.body.citesegment,
+      citerole: req.body.citerole,
+      admin: false,
+      role_id: 0
+    }).catch(function (err) {
+      log.error(err);
+      let errors = [];
+      for (let errName in err.errors) {
+        errors.push(err.errors[errName].message);
+      }
+      log.error(errors);
+      res.statusCode = 400;
+      return res.json({ err, errors });
+      // handle error;
+    });
+    let tokenValue = uuid.v4();
+    const verificationToken = VerificationToken.create({
+      user_id: user.id,
+      token: tokenValue,
+      verified: false
+    });
+    if (!verificationToken) {
+      res.statusCode = 404;
+      return res.json({ msg: "Couldn't create Verification Token" });
+    }
+    let url = config.default.lde.url + '/verify';
+    let text = `Olá, ${user.name}, seja bem vindo/a ao Laboratório de Dados Educacionais.\n\nClique neste link para confirmar sua conta: ${url}/${tokenValue}`;
+    // Send confirmation email
+    let mailOptions = {
+      to: `"${user.name} <${user.email}>"`,
+      subject: "Confirme seu cadastro - Laboratório de Dados Educacionais",
+      text
+    }
+    email(mailOptions, (err, info) => {
+      if (err) {
+        log.error(err);
+        res.json({ msg: 'Message not delivered, user created but not confirmed' });
+      }
+      if (info) {
+        log.info(`Message ${info.messageId} sent: ${info.response}`);
+        log.info(`Usuário ${user.email} foi criado`);
+      }
+      res.json({ msg: 'User created' });
+    });
+  }
+});
+
+userApp.put('/:id', passport.authenticate('bearer', { session: false }), async (req, res, next) => {
+  let user = await User.findByPk(req.params.id).catch(function (err) {
+    if (err) {
+      log.error(err);
+      return next({ err });
+    }
+  })
+  if (!user) {
+    res.statusCode = 404;
+    return next({
+      err: {
+        message: 'Usuário não encontrado'
+      }
+    });
+  }
+
+  user.email = req.body.email || user.email;
+  user.name = req.body.name || user.name;
+  user.nickname = req.body.nickname || user.nickname || user.name;
+  user.cep = req.body.cep || user.cep;
+  user.complement = req.body.complement || user.complement;
+  user.address = req.body.address || user.address;
+  user.phone = req.body.phone || user.phone;
+  user.schooling = req.body.schooling || user.schooling;
+  user.course = req.body.course || user.course;
+  user.segment = req.body.segment || user.segment;
+  user.role = req.body.role || user.role;
+  user.institutionName = req.body.institutionName || user.institutionName;
+  user.state = req.body.state || user.state;
+  user.city = req.body.city || user.city;
+  user.receive_email = req.body.receiveEmails || req.body.receive_emails || user.receive_email;
+  user.citesegment = req.body.citesegment || user.citesegment;
+  user.citerole = req.body.citerole || user.citerole;
+
+  if((!isEmpty(req.body.password)) && (!isEmpty(req.body.newpassword))){
+    if (req.body.password != req.body.newpassword) {
+      if (user.checkPassword(user, req.body.password)) {
+          await user.update({password:req.body.newpassword});
+        } 
+      else {
+        res.statusCode = 500;
+        return res.json({
+          error: {
+            message: 'A senha atual está incorreta'
+          }
+        });
+      }
+    } else {
+      res.statusCode = 500;
+      return res.json({
+        error: {
+          message: 'A nova senha é a mesma da senha atual'
+        }
+      });
+    }
+  }
+
+  user.save().catch(err => {
+    if (err) {
+      log.error(err);
+      return next({ message: 'Erro ao atualizar usuário' });
+    }})
+  let u = user.toJSON();
+  u._id = u.id;
+  u.institutionName = u.institution_name;
+  u.receiveEmails = u.receive_email;
+  u.receive_emails = u.receive_email;
+  delete u.id;
+  delete u.institution_name;
+  delete u.receive_email;
+  delete u.hashed_password;
+  delete u.salt;
+  delete u.password;
+  res.json({ user: u });
+
+});
+
+
+userApp.get('/reset/password', async (req, res, next) => {
+  let emailAddress = req.query.email;
+  let user = await User.findOne({ where: { email: emailAddress } }).catch(function (err) {
+    log.error(err);
+    let errors = [];
+    for (let errName in err.errors) {
+      errors.push(err.errors[errName].message);
+    }
+    log.error(errors);
+    res.statusCode = 400;
+    return res.json({ err, errors });
+    // handle error;
+  });
+  if (!user) {
+    res.statusCode = 404;
+    res.json({ msg: "O usuário não está cadastrado" });
+  }
+  else {
+    let tokenValue = uuid.v4();
+    const rt = await ResetToken.create({
+      user_id: user.id,
+      token: tokenValue,
+      reset: false
+    });
+    if (!rt) {
+      res.statusCode = 404;
+      return res.json({ msg: "Couldn't create Reset Password Token" });
+    }
+    let url = config.default.lde.url + '/reset-password';
+    let text = `Olá, ${user.name}.\n\nRecebemos uma solicitação para redefinir sua senha do Laboratório de Dados Educacionais. Clique neste link para redefinir a sua senha: ${url}/${tokenValue}`;
+    let mailOptions = {
+      to: `"${user.name} <${user.email}>"`,
+      subject: "Redefinição de Senha - Laboratório de Dados Educacionais",
+      text
+    }
+    console.log(mailOptions);
+    email(mailOptions, (err, info) => {
+      if (err) {
+        log.error(err);
+        res.json({ msg: 'Undelivered Reset Password Mail' });
+      }
+      res.json({ msg: 'Reset Password Mail Successfully Delivered' });
+    });
+  }
+});
+
+module.exports = userApp;
-- 
GitLab