Commit 6c285e0b authored by bhmeyer's avatar bhmeyer Committed by Matheus Horstmann

Resolve "Integrar o ensalador"

parent 265b0efa
......@@ -36,16 +36,18 @@ COPY --chown=node:node yarn.lock .
# RUN yarn install --production --frozen-lockfile --silent --non-interactive
RUN yarn install --frozen-lockfile --silent --non-interactive
ENV PATH=$PATH:/app/node_modules/.bin
EXPOSE 3000
## Compile ensalor here
RUN mkdir /app/bin/ && mkdir /app/bin/ensalador
COPY --chown=node:node bin/ensalador /app/bin/ensalador
RUN cd /app/bin/ensalador && cmake . && make && cd /app
# Bundle app source
COPY --chown=node:node . .
## Compile ensalor here
# cd /app/bin/ensalador && cmake . && make && make clean && cd /app
ENV PATH=$PATH:/app/node_modules/.bin
EXPOSE 3000
ENTRYPOINT ["/app/ensalamento-entrypoint.sh"]
......
......@@ -12,8 +12,6 @@ var lbTables = models.models;
var LOG_CREATE = false;
// console.log(app.models.Curso.scopes.disciplinas.modelFrom);
ds.automigrate(lbTables, function(err) {
if (err) throw err;
async.waterfall([
......@@ -25,13 +23,16 @@ ds.automigrate(lbTables, function(err) {
atribuiRoles,
criaRecursodesala,
criaTipodesala,
criaEnsalamento,
criaTurmas,
criaHorarios,
loadModelFromOldDB(old_db.get_professores, app.models.Professor, LOG_CREATE),
loadModelFromOldDB(old_db.get_departamentos, app.models.Departamento, LOG_CREATE),
loadModelFromOldDB(old_db.get_setores, app.models.Setor, LOG_CREATE),
loadModelFromOldDB(old_db.get_disciplinas, app.models.Disciplina, LOG_CREATE),
loadModelFromOldDB(old_db.get_equivalenciasDisciplinas, app.models.Equivalenciadisciplina, LOG_CREATE),
loadModelFromOldDB(old_db.get_cursos, app.models.Curso, LOG_CREATE),
loadRelationsFromOldDB(old_db.get_cursoDisciplina, app.models.Curso, "disciplinas", LOG_CREATE),
],
function(err) {
......@@ -44,7 +45,7 @@ ds.automigrate(lbTables, function(err) {
// Returns a function that populate a DB model with data loaded from
// old database (see migrate-old-schema.js and load-old-dabase.js codes)
function loadModelFromOldDB(oldDataAccess, model, create_log){
return function(cb){
return (cb) => {
oldDataAccess.then(data => {
async.each(data, (instance, callback) => {
if(create_log) console.log('Trying to insert:', instance);
......@@ -105,6 +106,97 @@ function loadRelationsFromOldDB(oldDataAccess, model, modelRelationName, create_
}
}
function criaTurmas(cb){
app.models.Turma.create([
{
"disciplinaCod":"CI055",
"data_inicio":"2019-02-26T13:58:50.150Z",
"data_fim":"2019-02-26T13:58:50.150Z",
"codigo": "t1",
"organizador":"a",
"vagas":49,
"ano":2019,
"periodo": 1
},
{
"disciplinaCod":"CI055",
"data_inicio":"2019-02-26T13:58:50.150Z",
"data_fim":"2019-02-26T13:58:50.150Z",
"codigo": "t2",
"organizador":"a",
"vagas":49,
"ano":2019,
"periodo": 1
},
{
"disciplinaCod":"CI055",
"data_inicio":"2019-02-26T13:58:50.150Z",
"data_fim":"2019-02-26T13:58:50.150Z",
"codigo": "t3",
"organizador":"a",
"vagas":49,
"ano":2019,
"periodo": 1
},
{
"disciplinaCod":"CI055",
"data_inicio":"2019-02-26T13:58:50.150Z",
"data_fim":"2019-02-26T13:58:50.150Z",
"codigo": "t4",
"organizador":"a",
"vagas":49,
"ano":2019,
"periodo": 1
},
], function(err, turmas){
turmas[0].horarios.add(1);
turmas[1].horarios.add(2);
turmas[2].horarios.add(3);
turmas[3].horarios.add(4);
cb(err);
}.bind(this));
}
function criaHorarios(cb){
app.models.Horario.create([
{
livre: false,
dia:2,
horario_inicial:1,
horario_final:2,
ensalamentoId:1
},
{
livre: false,
dia:3,
horario_inicial:1,
horario_final:2,
ensalamentoId:1
},
{
livre: false,
dia:4,
horario_inicial:1,
horario_final:2,
ensalamentoId:1
},
{
livre: false,
dia:2,
horario_inicial:1,
horario_final:2,
ensalamentoId:1
}
], function(err, horarios){
cb(err);
}.bind(this));
}
function criaEnsalamento(cb){
app.models.Ensalamento.create({}, function(err){
cb(err);
}.bind(this));
}
function criaRecursodesala(cb){
var recursos = [
......
ensalador @ d011c755
Subproject commit d011c755063a451ffcf55907453860f468c4dfd2
......@@ -103,8 +103,6 @@ class EnsalamentoTable {
}
});
});
}
......
......@@ -21,6 +21,7 @@
* Has and Belongs To Many : **Recursodesala**
* Belongs to : **Tipodesala** as `tipo`
* Belongs to : **Secretario** as `ownerId`
* Has Many : **Horario** as `horarios`
## Bloco
- Public: `True`
......@@ -83,13 +84,14 @@
* **$everyone** ROLE have all READ operations
* **admin** ROLE have all EXECUTE, READ and WRITE operations
- Relations
* Has and Belongs To Many : **Horario** as horarios
* `Model Disciplina has a relation Has Many on this`
## Turma
- Public: `True`
- Description: This model extend `Evento`
- Attributes
* horarios : **date** `Required`
* codigo : **string** `Required`
* data_inicio : **date** `Required`
* data_fim : **date** `Required`
- ACLs
......@@ -225,12 +227,12 @@
* All permissions not specified is `DENY`
* **admin** ROLE have all EXECUTE, READ and WRITE operations
- Relations
* Belongs To : **Departamento** as `departamentoCod`
* Belongs To : **Departamento** as `departamento`
* Has and Belongs To Many : **Turma**
## Usuario
- Public: `True`
- Attributes `
- Attributes
* nome : **string** `Required`
* restricao : **string**, default=`'change_password'`
* Used for restrict actions of user in some cases. For instance, when user
......@@ -240,3 +242,31 @@
* **admin** ROLE have all EXECUTE, READ and WRITE operations
- Relations
* Embeds One : **Secretario** as `secretario`
* Has Many : **Ensalamento** as `ensalamentos`
## Horario
- Public: `True`
- Attributes
* dia : **number** `Required`
* horario_inicial : **number** `Required`
* horario_final : **number** `Required`
* livre : **boolean** `Required`
* motivo : **string**
- ACLs
* All permissions not specified is `DENY`
* **$everyone** ROLE have all READ operations
* **admin** ROLE have all EXECUTE, READ and WRITE operations
- Relations
* Belongs To : **Sala** as `sala`
* Belongs To : **Ensalamento** as `ensalamento`
## Ensalamento
- Public: `True`
- Attributes
- ACLs
* All permissions not specified is `DENY`
* **$everyone** ROLE have all READ operations
* **admin** ROLE have all EXECUTE, READ and WRITE operations
- Relations
* Has Many : **Horario** as `horarios`
* Belongs To : **Usuario** as `usuario`
......@@ -23,7 +23,6 @@ module.exports = function(Disciplina) {
// Filter only the ids of equivalents courses
// The id can be on disciplina1 or disciplina2 columns
// console.log(eq[0].disciplina1.toString(), eq[0].disciplina2.toString());
var ret = eq.map(function(x){
var codigoDisciplina1 = x.__data.disciplina1;
......
......@@ -91,7 +91,6 @@
],
"methods": {
"prototype.addEquivalencia": {
"description": "add description",
"accepts": [
{
"arg": "disciplina_eq_code",
......@@ -99,28 +98,28 @@
}
],
"returns": {
"type":"object",
"type": "object",
"root": true
},
"description": "add description",
"http": {
"verb": "post",
"path": "/equivalencias"
}
},
"prototype.getEquivalencias": {
"description": "add description",
"accepts": [],
"returns": {
"arg": "data",
"type": "string"
},
"description": "add description",
"http": {
"verb": "get",
"path": "/equivalencias"
}
},
"prototype.deleteEquivalencia": {
"description": "add description",
"accepts": [
{
"arg": "disciplina_eq_code",
......@@ -128,9 +127,10 @@
}
],
"returns": {
"type":"object",
"root": true
"type": "object",
"root": true
},
"description": "add description",
"http": {
"verb": "delete",
"path": "/equivalencias"
......
'use strict';
var async = require('async');
var path = require("path");
var app = require('../../server/server');
const {Ensalador, EnsaladorPart, Schedule, ScheduleCollection, Room, RoomColection, CurrentSchedule} = require(path.resolve(__dirname,'../../bin/ensalador/ensalador.js'));
// Convert an instance of Horario into an Schedule (Ensalador) instance
function getScheduleInstance(h, cb){
var Horario = app.models.Horario;
Horario.getCode(h.id, (err,horarioCode) => {
if(err) return cb(err,null);
Horario.getTotalSize(h.id, (err,horarioSize) => {
if(err) return cb(err,null);
// TODO: Type, klass_id, department and course must be corrected
var schedule = new Schedule({
code: horarioCode,
id: h.id,
day: h.dia,
ini: h.horario_inicial,
end: h.horario_final,
size: horarioSize,
type: 1,
department: "d",
course: "c",
klass_id: 1
});
cb(null,schedule);
});
});
}
function getAllSchedulesInstances(newEnsalamento,cb){
var Horario = app.models.Horario;
// TODO: Filter by parameters specified by user
var queryHorario = {};
Horario.find(queryHorario, function(err, horarios){
// Transform the horarios to objects
horarios = horarios.map(h => {return h.toJSON()});
// Transform all Horario instances in Schedule (Ensalador)
async.map(horarios, getScheduleInstance, (err, schedules) => {
cb(err, new ScheduleCollection(newEnsalamento.id,schedules));
});
});
}
function getAllRoomsInstances(newEnsalamento,cb){
var Sala = app.models.Sala;
var querySala = {};
Sala.find(querySala, function(err, salas){
if(err) return cb(err,null);
var rooms = salas.map(s => {
s = s.toJSON();
// TODO: type must be an integer, but the model Sala contains a string
return new Room({
code: s.codigo,
id: s._id,
type: 1,
size: s.capacidade,
block: s.blocoCod,
lat: s.localizacao.lat,
log: s.localizacao.lng
});
});
cb(err, new RoomColection(newEnsalamento.id,rooms));
});
}
module.exports = function(Ensalamento) {
Ensalamento.ensalar = function(cb) {
var Horario = app.models.Horario;
// TODO: insert user id when create ensalamento instance
app.models.Ensalamento.create({}, function(err, newEnsalamento){
if(err) return cb(err,null);
getAllRoomsInstances(newEnsalamento,(err, rooms) => {
if(err) return cb(err,null);
getAllSchedulesInstances(newEnsalamento,(err,schedules) => {
if(err) return cb(err,null);
// Save the files into ensalador/ensalador_executions
// The id of new ensalamento is the identifier of
// yaml new files
rooms.save();
schedules.save();
var dataCurrent = schedules.toJSON().schedules.map(s =>{
return new Schedule(s)
});
var current = new CurrentSchedule(newEnsalamento.id, dataCurrent);
current.save();
// Heuristic used to execute
// Here you can specify the parameters of algorithm
var parts = [
new EnsaladorPart("Open", "open"),
new EnsaladorPart("Final", "exec",{day: [2,3,4,5,6,7]}),
new EnsaladorPart("Close", "close")
];
var e = new Ensalador(
parts,
rooms.getFilePath(),
schedules.getFilePath(),
current.getFilePath(),
);
e.execute((err,schedules) => {
if(err) return cb(err,null);
async.each(schedules,
(s,cb_s) => {
var query = {where:{id: s.id}};
Horario.findOne(query, (err, h) => {
if(err) return cb(err,null);
h.updateAttribute("salaCode", s.assigned, err => {
cb_s(err);
});
});
},
(err) => {
// If everything is ok
cb(err,schedules);
}
);
});
})
});
});
};
};
{
"name": "Ensalamento",
"plural": "ensalamentos",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {},
"validations": [],
"relations": {
"horarios": {
"type": "hasMany",
"model": "Horario",
"foreignKey": "ensalamentoId"
},
"usuario": {
"type": "belongsTo",
"model": "Usuario",
"foreignKey": "usuarioId"
}
},
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY",
"property": "*"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
},
{
"principalType": "ROLE",
"principalId": "admin",
"permission": "ALLOW",
"property": "*"
}
],
"methods": {
"ensalar": {
"accepts": [],
"returns": {
"type": "object",
"root": true
},
"description": "ensala todos os eventos",
"http": {
"verb": "post",
"path": "/ensalar"
}
}
}
}
'use strict';
var app = require('../../server/server');
module.exports = function(Horario) {
Horario.getCode = function(horario_id, cb){
var Turma = app.models.Turma;
var queryTurma = {
include: {
relation: 'horarios',
}
};
// TODO: Also, collect all Evento instances
// Query the model Turma and find all instances related to horario_id
// The Horario model cannot be used directly because
// the hasAndBelongsToMany is in Turma model
Turma.find(queryTurma, (err,turmas) => {
if(err) return cb(err,null);
var codes = turmas.map(t => {
var horarios_turma = t.horarios;
// Create an array with the codes of turmas
// If the turma is not related with horario_id, then the code
// is an empty string
for(var h in horarios_turma){
var horario = horarios_turma[h];
if(horario.id == horario_id) return t.codigo;
}
return "";
});
// Filter and join codes with "-"
codes = codes.filter(c => {return c != "";});
cb(err,codes.join("-"));
});
}
Horario.getTotalSize = function(horario_id, cb){
var Turma = app.models.Turma;
var queryTurma = {
include: {
relation: 'horarios',
}
};
// Query the model Turma and find all instances related to horario_id
// The Horario model cannot be used directly because
// the hasAndBelongsToMany is in Turma model
Turma.find(queryTurma, (err,turmas) => {
if(err) return cb(err,null);
// Create an array with the size (need to scheduled) of turmas
// If the turma is not related with horario_id, then the size is 0
var sizes = turmas.map(t => {
var horarios_turma = t.horarios;
for(var h in horarios_turma){
var horario = horarios_turma[h];
if(horario.id == horario_id) return t.vagas;
}
return 0;
});
// Sum all sizes
cb(err, sizes.reduce((acc,size)=>{return acc+size},0));
});
}
};
{
"name": "Horario",
"plural": "horarios",
"base": "PersistedModel",
"idInjection": true,
"options": {
"validateUpsert": true
},
"properties": {
"dia": {
"type": "number",
"required": true
},
"horario_inicial": {
"type": "number",
"required": true
},
"horario_final": {
"type": "number",
"required": true
},
"livre": {
"type": "boolean",
"required": true
},
"motivo": {
"type": "string"
}
},
"validations": [],
"relations": {
"sala": {
"type": "belongsTo",
"model": "Sala",
"foreignKey": "salaCode"
},
"ensalamento": {
"type": "belongsTo",
"model": "Ensalamento",
"foreignKey": "ensalamentoId"
}
},
"acls": [
{
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "DENY",
"property": "*"
},
{
"accessType": "READ",
"principalType": "ROLE",
"principalId": "$everyone",
"permission": "ALLOW"
},
{
"principalType": "ROLE",
"principalId": "admin",
"permission": "ALLOW",
"property": "*"
}
],
"methods": {}
}
......@@ -3,30 +3,33 @@
Models are used in a lot of script, so we created this file to unify.
**/
var models = [
'Usuario',
'Secretario',
'AccessToken',
'ACL',
'RoleMapping',
'Role',
'Sala',
'Bloco',
'Disciplina',
'Equivalenciadisciplina',
'Evento',
'Turma',
'Recursodesala',
'SalaRecursodesala',
'DisciplinaRecursodesala',
'Tipodesala',
'Orgao',
'Setor',
'Departamento',
'Curso',
"CursoDisciplina",
"Professor",
"ProfessorTurma"
];
'Usuario',
'Secretario',
'AccessToken',
'ACL',
'RoleMapping',
'Role',
'Sala',
'Bloco',
'Disciplina',
'Equivalenciadisciplina',
'Evento',
'Turma',
'Recursodesala',
'SalaRecursodesala',
'DisciplinaRecursodesala',
'Tipodesala',
'Orgao',
'Setor',
'Departamento',
'Curso',
"CursoDisciplina",
"Professor",
"ProfessorTurma",
"Horario",
"Ensalamento",
"EventoHorario",
];
/**
Some scripts need the lower case version of it
......
......@@ -66,6 +66,14 @@
"type": "belongsTo",
"model": "Secretario",
"foreignKey": "ownerId"
},
"horarios": {
"type": "hasMany",
"model": "Horario",
"foreignKey": "salaId",
"options": {
"nestRemoting": true
}
}
},
"acls": [
......@@ -93,7 +101,6 @@
"principalId": "$owner",
"permission": "ALLOW"
}
],
"methods": {}
}
......@@ -57,13 +57,13 @@
"principalId": "admin",
"permission": "ALLOW",
"property": "*"
},
{
"principalType": "ROLE",
"principalId": "comissao",
"permission": "ALLOW",
"property": "*"
}
},
{
"principalType": "ROLE",