Commit 10268f98 authored by bhmeyer's avatar bhmeyer

Resolve "Incrementar sistema de grade curricular"

parent e4f535e4
......@@ -33,6 +33,7 @@ django-tests:
- python3 manage.py makemigrations submission
- python3 manage.py makemigrations student
- python3 manage.py makemigrations admission
- python3 manage.py makemigrations grid
- python3 manage.py migrate
- python3 manage.py test
......@@ -2,6 +2,6 @@
# If ANY of this commands fails (return != 0) the container will be down
bash ./docker_scripts/wait_for_postgres.sh
cd src
python manage.py makemigrations degree admission educator submission course
python manage.py makemigrations degree admission educator submission course grid
python manage.py migrate
python manage.py runserver 0.0.0.0:8000
......@@ -2,7 +2,7 @@
# If ANY of this commands fails (return != 0) the container will be down
bash ./docker_scripts/wait_for_postgres.sh
cd src
python manage.py makemigrations degree admission educator submission course
python manage.py makemigrations degree admission educator submission course grid
python manage.py migrate
python manage.py collectstatic --noinput
......
from django.core.management.base import BaseCommand
from grid.models import Grid
from grid.generate_grid import generate_grid
class Command(BaseCommand):
help = 'Makes one specific analysis'
def add_arguments(self, parser):
parser.add_argument('grid_id', type=int)
def handle(self, *args, **options):
id = options['grid_id']
grid = Grid.objects.get(pk=id)
generate_grid(grid, False)
......@@ -47,6 +47,7 @@ INSTALLED_APPS = [
'public',
'degree',
'educator',
'grid',
'admission',
'course',
'student',
......
......@@ -66,8 +66,16 @@
<br>
{% for degree in degrees_last_submissions %}
<div class="row">
<div class="col-xl-4">{{degree.name}} ({{degree.code}})</div>
<div class="col-xl-4">
<a href="{% url 'degree:index' submission_id=degree.last_submission.id %}">Última análise</a><br>
</div>
<div class="col-xl-4">
<a href="{% url 'grid:GridList' degree_code=degree.code %}">Grade Curricular</a><br>
</div>
</div>
<a href="{% url 'degree:index' submission_id=degree.last_submission.id %}">{{degree.name}} ({{degree.code}})</a><br>
{% endfor %}
{% endblock content %}
......@@ -2,7 +2,7 @@
from django.conf.urls import include, url
from django.contrib import admin
from . import views
from adega import views
from django.views.generic import RedirectView
......@@ -17,6 +17,8 @@ urlpatterns = [
url(r'^submission/', include('submission.urls', namespace='submission')),
url(r'^grid/(?P<degree_code>\w+)/', include('grid.urls', namespace='grid')),
url(r'^student/(?P<submission_id>\w*)/', include('student.urls', namespace='student')),
url(r'^degree/(?P<submission_id>\w*)/', include('degree.urls', namespace='degree')),
......
......@@ -5,10 +5,15 @@ from django.contrib.auth.models import User
class Degree(models.Model):
name = models.CharField(max_length=40)
code = models.CharField(max_length=40)
code = models.CharField(max_length=40, unique=True)
manager = models.ForeignKey(User)
# grids = models.ForeignKey(Grid,
# on_delete=models.CASCADE,
# related_name = "degree",
# related_query_name = "degree")
#
def __str__(self):
return self.name
......
......@@ -116,8 +116,9 @@
<div id="graficos container">
<!-- TODO: Add dynamic grid for each degree -->
{% for dg in dg_list %}
<div class="col-md-12">
<h3>Grade curricular
<h3>Grade curricular - Versão: {{dg.version}}
<a tabindex="0" class="fa fa-info-circle" data-toggler="popover" data-html="true"
data-content="Informações gerais sobres as disciplinas do curso de graduação.<br>
<b>Passe o mouse</b> sobre os códigos para verificar os nomes completos
......@@ -129,20 +130,16 @@
quanto maior a taxa de aprovação mais verde,
quanto menor a taxa de aprovação mais vermelho. "></a>
</h3>
<div class="row" id="grid">
<div class="row" id="grid_{{dg.version}}">
<br>
<div class="grade col-md-10">
{% for semester in grid_info %}
{% for semester in dg.grid_info %}
<div class="semestre">
<div class="grade-head">{{forloop.counter}}º</div>
{% for course in semester %}
<div id="grid_course_course_{{course.code}}"
code={{course.code}}
data-toggle="tooltip"
data-placement="top"
title="{{course.name}}"
class="materia {{ course.situation }}"
>
<div id="grid_{{dg.version}}_course_course_{{course.code}}" code={{course.code}}
data-toggle="tooltip" data-placement="top" title="{{course.name}}" class="materia {{ course.situation }}">
<div class="info">
{% if course.is_real_code %}
<span class="name">
......@@ -178,7 +175,7 @@
<div class="col-md-2">
<div class="my-4">
<label class="p-0 switch mt-1">
<input type="checkbox" class="toggleheat invisible" onchange="toggleHeatmap(this)">
<input type="checkbox" class="toggleheat invisible" onchange="toggleHeatmap_{{dg.version}}(this)">
<span class="slider round"></span>
</label>
<span id="statusHM">Heatmap off</span>
......@@ -190,6 +187,7 @@
</div>
</div>
<br>
{% endfor %}
<div class="row py-3">
<div class="col-md-12">
......@@ -266,39 +264,54 @@
{% block js-foot %}
<script>
var grid_info = {{grid_info|safe_js}};
var prerequisites = {{prerequisites|safe_js}};
var prerequisites_rev = {{prerequisites_rev|safe_js}};
var prerequisites = {
{% for dg in dg_list %}
"{{dg.version}}": {{dg.prerequisites|safe_js}},
{% endfor %}
};
var prerequisites_rev = {
{% for dg in dg_list %}
"{{dg.version}}": {{dg.prerequisites_rev|safe_js}},
{% endfor %}
};
// ------------------------------- CALCULA gradiente do HEATMAP
var taxas = [];
for (s in grid_info)
for (c in grid_info[s])
{% for dg in dg_list %}
var grid_info = {{dg.grid_info|safe_js}};
for (s in grid_info){
for (c in grid_info[s]){
if (grid_info[s][c].detail)
taxas.push( grid_info[s][c].detail.taxa_aprovacao );
}
}
{% endfor %}
gradiente = chroma
.scale(['#FF6666', '#FFFF99', '#CCFF99']) // red, yellow, green
.domain([Math.min(...taxas), Math.max(...taxas)]);
function toggleHeatmap(fecho) {
{% for dg in dg_list %}
function toggleHeatmap_{{dg.version}}(fecho) {
var grid_info = {{dg.grid_info|safe_js}};
for (s in grid_info)
for (c in grid_info[s]) {
course = grid_info[s][c];
if (course.detail) {
if($(fecho).is(":checked")) {
$('#statusHM').text("Heatmap on");
$("#grid_course_course_"+course.code)
$("#grid_{{dg.version}}_course_course_"+course.code)
.css("backgroundColor", gradiente(course.detail.taxa_aprovacao));
}
else {
$('#statusHM').text("Heatmap off");
$("#grid_course_course_"+course.code).css("backgroundColor", "white");
$("#grid_{{dg.version}}_course_course_"+course.code).css("backgroundColor", "white");
}
}
}
}
{% endfor %}
// Add mouseenter and mouseleave events to grid cells
......@@ -308,26 +321,36 @@
// Mouse Enter event
$(".materia").hover(function(){
var code = $(this).attr("code");
for(var i in prerequisites[code]){
var code2 = prerequisites[code][i];
$("#grid_course_course_"+code2).addClass('materia_prerequisite');
for(var version in prerequisites){
for(var i in prerequisites[version][code]){
var code2 = prerequisites[version][code][i];
$("#grid_"+version+"_course_course_"+code2).addClass('materia_prerequisite');
}
}
for(var i in prerequisites_rev[code]){
var code2 = prerequisites_rev[code][i];
$("#grid_course_course_"+code2).addClass('materia_posrequisite');
for(var version in prerequisites_rev){
for(var i in prerequisites_rev[version][code]){
var code2 = prerequisites_rev[version][code][i];
$("#grid_"+version+"_course_course_"+code2).addClass('materia_posrequisite');
}
}
$(this).addClass('materia_selected');
},
// Mouse Leave event
function(){
var code = $(this).attr("code");
for(var i in prerequisites[code]){
var code2 = prerequisites[code][i];
$("#grid_course_course_"+code2).removeClass('materia_prerequisite');
for(var version in prerequisites){
for(var i in prerequisites[version][code]){
var code2 = prerequisites[version][code][i];
$("#grid_"+version+"_course_course_"+code2).removeClass('materia_prerequisite');
}
}
for(var i in prerequisites_rev[code]){
var code2 = prerequisites_rev[code][i];
$("#grid_course_course_"+code2).removeClass('materia_posrequisite');
for(var version in prerequisites_rev){
for(var i in prerequisites_rev[version][code]){
var code2 = prerequisites_rev[version][code][i];
$("#grid_"+version+"_course_course_"+code2).removeClass('materia_posrequisite');
}
}
$(this).removeClass('materia_selected');
}
......
......@@ -23,30 +23,40 @@ def index(request, submission_id):
submission = Submission.objects.get(id=submission_id)
degree = submission.degree
degree_data = get_degree_information(request.session, degree, submission_id=submission_id)
analysis_result = get_list_courses(request.session, degree, submission_id)
courses_list = analysis_result["cache"]
dg = DegreeGrid(DegreeGrid.bcc_grid_2011)
grid_info = dg.get_degree_situation(courses_list)
dg_list_context = []
# dg = DegreeGrid(DegreeGrid.bcc_grid_2011)
dg_list = DegreeGrid.get_degree_grid_list(degree.code)
for dg in dg_list:
dg = DegreeGrid(dg)
grid_info = dg.get_degree_situation(courses_list)
prerequisites = dg.grid_detail.prerequisites
prerequisites_rev = {}
for c1 in prerequisites:
for c2 in prerequisites[c1]:
if not (c2 in prerequisites_rev):
prerequisites_rev[c2] = []
prerequisites_rev[c2].append(c1)
dg_list_context.append({
"grid_info": grid_info,
"prerequisites": prerequisites,
"prerequisites_rev": prerequisites_rev,
"version": dg.grid_detail.version
})
prerequisites = dg.grid_detail.prerequisites
prerequisites_rev = {}
for c1 in prerequisites:
for c2 in prerequisites[c1]:
if not (c2 in prerequisites_rev):
prerequisites_rev[c2] = []
prerequisites_rev[c2].append(c1)
degree_data = get_degree_information(request.session, degree, submission_id=submission_id)
return render(request, "degree/index.html", {
"submission": submission,
"degree": degree,
"degree_data": degree_data,
"situations_pass": situations_pass,
"situations_fail": situations_fail,
"grid_info": grid_info,
"prerequisites": prerequisites,
"prerequisites_rev": prerequisites_rev,
"dg_list": dg_list_context
})
from django.contrib import admin
from grid.models import Grid #, GridCourse, GridPeriod
admin.site.register(Grid)
# admin.site.register(GridCourse)
# admin.site.register(GridPeriod)
from django.apps import AppConfig
class GridConfig(AppConfig):
name = 'grid'
from django import forms
from datetime import datetime
from grid.models import Grid
class GridForm(forms.ModelForm):
disciplinas = forms.FileField()
equivalencias = forms.FileField()
version = forms.IntegerField(initial=datetime.now().year)
class Meta:
model = Grid
fields = [
'version',
'disciplinas',
'equivalencias',
'degree'
]
import pandas as pd
import numpy as np
from grid.models import Grid #, GridCourse, GridPeriod
def create_periods(disciplinas, grid):
periods = {}
for index,i in disciplinas.drop_duplicates("PERIODO_IDEAL").iterrows():
period = GridPeriod.objects.create(grid=grid, number=i.PERIODO_IDEAL)
periods[str(i.PERIODO_IDEAL)] = period
return periods
def create_courses(disciplinas,periods, grid):
course_list = disciplinas.drop_duplicates("COD_DISCIPLINA")
courses = {}
# create courses
for index, i in course_list.iterrows():
if i.DESCR_ESTRUTURA != "optativas":
course = GridCourse.objects.create(name=i.NOME_DISCIPLINA,
_type="Obrigatórias",
code=i.COD_DISCIPLINA,
period=periods[str(i.PERIODO_IDEAL)])
else:
course = GridCourse.objects.create(name=i.NOME_DISCIPLINA,
_type="optativas",
code=i.COD_DISCIPLINA,
grid=grid)
courses[i.COD_DISCIPLINA] = course
# add prerequisites
# prerequisites = disciplinas.groupby(["COD_DISCIPLINA"])
# for j in prerequisites:
# course = courses[j[0]]
# print(course)
# for k in j[1].drop_duplicates("COD_PRE_REQ"):
# courses[k].prerequisites_set.add(course)
# #course.prerequisites_set.add(courses[k.COD_DISCIPLINA])
#
def generate_grid(grid, debug):
disciplinas = pd.read_excel(grid.path()+"/disciplinas.xls",
encoding='ISO-8859-1')
equivalencias = pd.read_excel(grid.path()+"/equivalencias.xls",
encoding='ISO-8859-1')
periods = create_periods(disciplinas, grid)
courses = create_courses(disciplinas, periods, grid)
return True
from django.db import models
from degree.models import Degree
from os import path
from django.conf import settings
import json
def get_path(instance, filename):
return '{}/{}/{}'.format(instance.degree.code, instance.id, filename)
class Grid(models.Model):
# version = models.IntegerField()
version = models.CharField(max_length=40, unique=True)
data_as_string = models.TextField()
degree = models.ForeignKey(
Degree,
on_delete=models.CASCADE,
related_name="grids",
related_query_name="grids",
)
def __str__(self):
return "Curso: {} Versão: {}".format(self.degree, self.version)
# def get_courses(self):
# courses = {}
# for period in self.periods.all():
# for course in period.courses.all():
# courses[course.code] = course.name
# return courses
# def get_periods(self):
# periods = []
# for period in self.periods.all():
# periods.append(period.get_courses())
# return periods
# def get_equiv_code(self):
# equiv_code = {}
# for course in self.optatives.all():
# equiv_code[course.code] = ["OPT"]
# return equiv_code
# def get_grid(self):
# grid = {"year": self.version,
# "grid": self.get_periods(),
# "repeated_codes": ["OPT"],
# "fake_codes": ["OPT", "TG I", "TG II"],
# "code_to_name": self.get_courses(),
# "equiv_codes": self.get_equiv_code()
# }
# return grid
# #def save(self, *args, **kwargs):
# # """
# # Sobrescrita do metodo save.
# # É necesário rescrever o metodo save, para poder obter o id da instancia
# # enquanto o modelo ainda não foi salvo no banco de dados.
# # """
# # if self.id is None:
# # disciplinas = self.disciplinas
# # equivalencias = self.equivalencias
# # self.disciplinas = None
# # self.equivalencias = None
# # super(Grid, self).save(*args, **kwargs)
# # self.disciplinas = disciplinas
# # self.equivalencias = equivalencias
# # # kwargs.pop('force_insert')
# # super(Grid, self).save(*args, **kwargs)
# def path(self):
# return path.join(settings.MEDIA_ROOT, self.degree.code, str(self.id))
# class GridPeriod(models.Model):
# number = models.IntegerField()
# grid = models.ForeignKey(
# Grid,
# on_delete=models.CASCADE,
# related_name="periods",
# related_query_name="periods"
# )
# def get_courses(self):
# courses = []
# for course in self.courses.all():
# courses.append(course.code)
# return courses
# def __str__(self):
# return "Grade: {}-{}\n Periodo: {}\nDisciplinas:\n {}".format(self.grid.version,
# self.grid.id,
# self.number,
# self.get_courses())
# class GridCourse(models.Model):
# name = models.CharField(max_length=200)
# _type = models.CharField(max_length=32)
# code = models.CharField(max_length=32)
# prerequisites = models.ForeignKey("self",related_name="pre_requisites",related_query_name="pre_requisites", blank=True, null=True,)
# equivalency = models.ForeignKey("self",related_name="equiv", related_query_name="equiv", blank=True, null=True,)
# # prerequisites = models.ManyToManyField("self", through="GridCourseRequisite",
# # symmetrical=False, )
# # equivalences = models.ManyToManyField("self", through="GridCourseEquivalence",
# # symmetrical=True)
# period = models.ForeignKey(
# GridPeriod,
# on_delete=models.CASCADE,
# related_name="courses",
# related_query_name="courses",
# blank=True,
# null=True,
# )
# grid = models.ForeignKey(
# Grid,
# on_delete=models.CASCADE,
# related_name="optatives",
# related_query_name="optatives",
# blank=True,
# null=True,
# )
# def __str__(self):
# return "{} {}".format(self.name, self.code)
# class ContactRelationship(models.Model):
# types = models.ManyToManyField('RelationshipType', blank=True,
# related_name='contact_relationships')
# from_contact = models.ForeignKey('Contact', related_name='from_contacts')
# to_contact = models.ForeignKey('Contact', related_name='to_contacts')
# class Meta:
# unique_together = ('from_contact', 'to_contact')
{% extends 'base.html' %}
{% load static %}
{% block content %}
<div class="row">
<div class="col-6">
<h3>Adicionar disciplina</h3>
</div>
<div class="col-6">
<h3>Disciplinas adicionadas</h3>
</div>
</div>
<div class="row">
<div class="col-5">
<form action="grid_create_submit" method="get" accept-charset="utf-8">
<div class="form-row">
<div class="form-group col-6">
<label for="course_name">Nome disciplina</label>
<input type="text" class="form-control" id="course_name" aria-describedby="text" placeholder="Nome da disciplina">
</div>
<div class="form-group col-6">
<label for="course_code">Código disciplina</label>
<input type="text" class="form-control" id="course_code" aria-describedby="text" placeholder="Código da disciplina">
</div>
</div>
<div class="form-row">
<div class="form-group col-6">
<label for="course_period">Periodo</label>
<input type="number" class="form-control" id="course_period" aria-describedby="number" value=1 >
</div>
<div class="form-group col-6">
<label for="course_type">Tipo da disciplina</label>
<select class="form-control" id="course_type">
<option>Obrigatória</option>
<option>Optativa</option>
<option>Trabalho de conclusão de curso</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group col-6">
<label for="course_code">Pré requisitos</label>
<input type="text" class="form-control"
id="course_prerequisite" aria-describedby="text"
placeholder="código separado por ,">
</div>
<div class="form-group col-6">
<label for="course_code">Disciplinas equivalentes</label>
<input type="text" class="form-control" id="course_equivalent"
aria-describedby="text" placeholder="código separado por ,">
</div>
</div>
<button type="button" class="btn btn-primary" onclick="add_course()">Adicionar disciplina</button>
</form>
</div>
<div class="col-7" >
<table class="display" style="width:100%" id="courses_table_g" >
<thead>
<tr>
<th scope="col">Nome</th>
<th scope="col">Código</th>
<th scope="col">Periodo</th>
<th scope="col">Tipo</th>
<th scope="col">Pre requisito</th>
<th scope="col">Equivalente</th>
<th scope="col">Ações</th>
</tr>
</thead>
<tbody id="courses_table">
</tbody>
</table>
</div>
</div>
<br>
<h2>Informações gerais da grade</h2>
<div class="col-8">
<form id="genGrid">
{% csrf_token %}
<div class="form-row">
<div class="col-lg-8 col-m-12 form-group">
<label for="relative_year">Versão da grade</label>
<input type="text" class="form-control" name="version" id="version" />
</div>
</div>
<div class="form-row">
<div class="col-lg-3 col-md-4 col-sm-4 offset-lg-2">
<button class="btn btn-success btn-block" type="submit" >Gerar grade</button>
</div>
<div class="col-lg-3 col-md-4 col-sm-4">
<button class="btn btn-danger btn-block" type="submit">Cancelar</button>
</div>
</div>
</form>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<script>
var optatives = [];
var courses = {};
function grid_table() {
var table = document.createElement("TABLE");
table.innerHTML = "";
//cria o header
var thead = document.createElement("THEAD")
var tr_head = document.createElement("TR")
//var periods = Object.Keys(courses);
var periods = Object.keys(courses).sort();
for (var i in periods) {
var headerCell = document.createElement("TH");
headerCell.innerHTML = periods[i];
tr_head.appendChild(headerCell);
}
thead.appendChild(tr_head);
table.appendChild(thead);
// Cria o body
var tbody = document.createElement("TBODY");
for (var i in periods) { //itera os periodos
var period = periods[i];
var tr = document.createElement("TR");
//for (var j=0; j<Object.keys(cIourses[period]).length; j++) { //itera as disciplinas do periodo
for (var j in courses[period]) { //itera as disciplinas do periodo
var course = courses[period][j];
var td = document.createElement("TD");
td.innerHTML = course["course_code"];
tr.appendChild(td);
}
console.log(period)
tbody.appendChild(tr);
}
table.appendChild(tbody);
var dvTable = document.getElementById("grid_table");
dvTable.innerHTML = "";
dvTable.appendChild(table);
}
function add_course() {
var course = {};
var field = ["course_name","course_code","course_period","course_type","course_prerequisite","course_equivalent"];
// carrega os valores do formulario de adicionar disciplina
for (var i in field ) {
//console.log(field[i]);
course[field[i]] = document.getElementById(field[i]).value;
//course.push(document.getElementById(field[i]).value)
// limpa o formulário, mas mantem o periodo e o tipo da disciplina
if (field[i] !== "course_type" && field[i] != "course_period")
document.getElementById(field[i]).value = "";
}
period = course["course_period"].toString();
if(period in courses)
courses[period].push(course);
else
courses[period] = [course];<