Commit e4aaee56 authored by Odair M.'s avatar Odair M.

Merge branch '114-integrar-analises-relacionada-ao-curso-com-o-sistema-web' into 'development'

Resolve "integrar analises relacionada ao curso com o sistema web"

See merge request adega/adega!43
parents d2a9892f e362ce07
// This module simplify the use of Plotly library on this project
class AdegaChart{
constructor(config){
......@@ -9,6 +11,8 @@ class AdegaChart{
this.legend = config.legend || null;
this.barmode = config.barmode || "stack";
if(config.data == null){
this.data_x = config.data_x;
......@@ -18,8 +22,8 @@ class AdegaChart{
else{
this.data_x = [];
this.data_y = [];
var first_element;
for (first_element in this.data) break;
var first_element = Object.keys(this.data)[0];
first_element = this.data[first_element];
var multiplePlots = Array.isArray(first_element);
......@@ -53,7 +57,7 @@ class AdegaChart{
this.type = config.type || "scatter";
this.title = config.title || "";
if(typeof(this.data_y[0]) == "number"){
this.data_y = [this.data_y];
this.type = [this.type];
......@@ -63,6 +67,9 @@ class AdegaChart{
this.error_y = [this.error_y];
}
this.data_axis_y = config.data_axis_y || this.data_y.map(function(x){return "y1";});
this.reloadGraph();
}
......@@ -88,10 +95,11 @@ class AdegaChart{
continue;
data.push(
{
x: this.data_x,
y: this.data_y[i],
type: this.type[i],
fill: this.fill
x: this.data_x,
y: this.data_y[i],
type: this.type[i],
fill: this.fill,
yaxis: this.data_axis_y[i]
}
);
......@@ -110,9 +118,34 @@ class AdegaChart{
var layout = {
title: this.title,
showlegend: true
showlegend: true,
yaxis: {
// title: 'yaxis title',
rangemode: 'tozero'
// overlaying: 'y'
},
yaxis2: {
// title: 'yaxis2 title',
// titlefont: {color: 'rgb(148, 103, 189)'},
// tickfont: {color: 'rgb(148, 103, 189)'},
overlaying: 'y1',
side: 'right',
rangemode: 'tozero'
},
barmode: this.barmode
};
Plotly.newPlot(this.div_target, data, layout);
}
}
AdegaChart.sort_object_by_key = function(obj){
keys = Object.keys(obj);
keys.sort();
values = keys.map(function(x){
return obj[x];
});
return [keys,values];
}
\ No newline at end of file
<div class="sidebar left hidden-sm hidden-xs">
<ul class="list-sidebar">
<li><a href="{% url 'student:index' degree_id=degree.code%}">Alunos</a></li>
<li><a class="btn btn-primary text-left" href="{% url 'student:index' degree_id=degree.code%}">Alunos</a></li>
<li style="display: block;">
<div>
<a href="{% url 'course:index' degree_id=degree.code%}">Disciplinas</a>
<a href="course" class="drop" data-toggle="collapse" data-target="#side-disciplinas">
<a class="btn btn-primary text-left" href="{% url 'course:index' degree_id=degree.code%}">Disciplinas</a>
<a class="btn btn-primary text-left" href="course" class="drop" data-toggle="collapse" data-target="#side-disciplinas">
<span class="rotate"><i class="fa fa-angle-left"></i></span>
</a>
</div>
<ul class="sub-menu collapse" id="side-disciplinas">
<li><a href="#">Comparar</a></li>
<li><a class="btn btn-primary text-left disabled" href="compare">Comparar</a></li>
</ul>
</li>
<li class="disabled"><a href="#">Professores</a></li>
<li class="disabled"><a href="#">Turmas</a></li>
<li><a href="{% url 'admission:index' degree_id=degree.code%}">Turmas de Ingresso</a></li>
<li><a class="btn btn-primary disabled text-left" href="#">Professores</a></li>
<li><a class="btn btn-primary disabled text-left" href="#">Turmas</a></li>
<li><a class="btn btn-primary text-left" href="{% url 'admission:index' degree_id=degree.code%}">Turmas de Ingresso</a></li>
<li style="display: block;" >
<a href="#" data-toggle="collapse" data-target="#side-outros" style="display:flex" class="drop">
......@@ -22,7 +22,7 @@
<span style="padding-top:3px" class="rotate"><i class="fa fa-angle-left"></i></span>
</a>
<ul class="sub-menu collapse" id="side-outros">
<li><a href="#">Cepe 96/15</a></li>
<li><a class="btn btn-primary disabled text-left" href="#">Cepe 96/15</a></li>
</ul>
</li>
</ul>
......
......@@ -30,7 +30,9 @@
<section id="wrap-page" class="container-fluid">
<div class="row" style="min-height:100%">
<aside class="col-md-2 sidebar">
{% include 'adega/sidebar.html' %}
{% if not hide_navbar %}
{% include 'adega/sidebar.html' %}
{% endif %}
</aside>
<section class="col-md-10">
{% if messages %}
......
......@@ -10,7 +10,7 @@ from django.contrib.auth import logout as process_logout
def dashboard(request):
degree = request.user.educator.degree.all()
return render(request, 'adega/dashboard.html', {'title': 'Dashboard',
"degrees":degree
"degrees":degree, "hide_navbar": True
})
......
......@@ -78,53 +78,21 @@
</div>
{% endcomment %}
{% comment %}
<div id="graficos">
<div class="row">
<div class="col-md-6">
<h3>Quantidade alunos/IRA</h3>
<canvas id="ira_semestral" data-data='{{ degree_data.ira_medio_grafico|safe }}'></canvas>
</div>
<div class="col-md-6">
<h3>Quantidade evasão/período</h3>
<canvas id="evasao_semestre" data-data='{{ degree_data.evasao_grafico|safe }}'></canvas>
</div>
<!--
<div class="col-md-6">
<h3>Nota Média/Periodo</h3>
<canvas id="nota_media_semestre"
data-data='{{ degree_data.media_disc|safe }}'></canvas>
</div> essa analise foi feita de uma maneira ruim no adega
antigo -->
<!-- analises sa02 -->
<div class="col-md-6">
<h3>Alunos/Periodo</h3>
<canvas id="aluno_periodo"
data-data='{{ degree_data.aluno_periodo|safe }}'></canvas>
</div>
<div class="col-md-6">
<h3>Nota Média/Periodo</h3>
<canvas id="nota_media_periodo"
data-data='{{ degree_data.nota_media_periodo|safe }}'></canvas>
</div>
<div class="col-md-6">
<h3>Taxa Aprovação/Periodo</h3>
<canvas id="taxa_aprovacao_periodo"
data-data='{{ degree_data.taxa_aprovacao_periodo|safe }}'></canvas>
</div>
<!-- fim analise sa02 -->
<div class="col-md-12">
<h3>IRA/Semestre</h3>
<div id="ira_semestral"></div>
</div>
<div class="col-md-12">
<h3>Quantidade de alunos/Semestre</h3>
<div id="students_per_semester"></div>
</div>
</div>
</div>
{% endcomment %}
</div>
</div>
</div>
<div id="myChart"></div>
<div id="myChart2"></div>
{% endblock content %}
......@@ -133,23 +101,45 @@
{% block js-foot %}
<script>
var admission_info = {{admission_info|safe}};
// ira_per_semester contains an object with year/semester
// as keys, and an tuple (mean,std) as values
// So, we need to transform this data before use on AdegaChart
var sorted = AdegaChart.sort_object_by_key(admission_info.ira_per_semester);
ira_per_semester_x = sorted[0];
ira_per_semester_y = sorted[1];
ira_per_semester_error = [];
ira_per_semester_y = ira_per_semester_y.map(function(x){
ira_per_semester_error.push(x[1]);
return x[0];
});
var chart1 = new AdegaChart({
data_x: [0,1,2],
data_y: [6,10,2],
div_target: "myChart",
error_y: [1,2,3],
//type: "bar",
title: "Exemplo 1"
data_x: ira_per_semester_x,
data_y: ira_per_semester_y,
error_y: ira_per_semester_error,
div_target: "ira_semestral",
type: "scatter",
legend: "IRA",
title: "IRA/Semestre"
});
var chart1 = new AdegaChart({
data_x: [0,1,2],
data_y: [[6,10,2], [7,9,2.5]],
div_target: "myChart2",
error_y: [[1,2,3], [1.5,0.1,0.5]],
type:["bar", "scatter"],
title: "Exemplo 2",
legend: ["linha 1", "linha 2"]
var sorted = AdegaChart.sort_object_by_key(admission_info.students_per_semester);
students_per_semester_x = sorted[0];
students_per_semester_y = sorted[1];
var chart2 = new AdegaChart({
data_x: students_per_semester_x,
data_y: students_per_semester_y,
div_target: "students_per_semester",
type:["scatter"],
title: "Quantidade de alunos/Semestre",
legend: "Quantidade de alunos"
});
</script>
......
......@@ -28,7 +28,7 @@
{{ ti.ano }}/{{ ti.semestre }}
</a>
</td>
<td>{{ti.ira|floatformat:2}} &plusmn {{ti.desvio_padrao|floatformat:2}}</td>
<td>{{ti.ira|floatformat:2}} &plusmn {{ti.std|floatformat:2}}</td>
<td> NO </td>
<td> NO </td>
<td> NO </td>
......
......@@ -13,8 +13,15 @@ def detail(request, degree_id, ano, semestre):
if not (degree in request.user.educator.degree.all()):
return redirect("adega:dashboard")
for admission in get_list_admission(request.session, degree):
if(admission["ano"] == ano and admission["semestre"] == semestre):
admission_info = admission
break
return render(request, 'admission/detail.html',{
"degree": degree
"degree": degree,
"admission_info": admission_info
})
......
......@@ -116,34 +116,50 @@
{% endif %}</td>
</td>
</table>
<div id="graficos">
<div class="row">
<div class="col-md-12">
<h3>Indice de aprovação/Semestre</h3>
<div id="aprovacao_semestre"></div>
</div>
<div class="col-md-12">
<h3>Quantidade de Alunos/Vezes Cursadas</h3>
<div id="qtd_cursada_aprov"></div>
</div>
</div>
</div>
</div>
<div id="aprovacao_semestre"></div>
<div id="myChart2"></div>
{% endblock content %}
{% block js-foot %}
<script>
var grafico_qtd_cursada_aprov = AdegaChart.sort_object_by_key(
{{analysis_result.grafico_qtd_cursada_aprov|safe}}
);
var grafico_qtd_cursada_aprov_x = grafico_qtd_cursada_aprov[0];
var grafico_qtd_cursada_aprov_y = grafico_qtd_cursada_aprov[1];
var chart1 = new AdegaChart({
data: {{analysis_result.aprovacao_semestral|safe}},
div_target: "aprovacao_semestre",
//type: "bar",
title: "Índice de aprovação/Semestre",
title: "Indice de aprovação/Semestre",
fill: "none",
legend: ["Aprovação %","Quantidade de alunos"]
legend: ["Taxa de aprovação","Quantidade de alunos"],
type:["scatter", "bar"],
data_axis_y: ["y2", "y"]
});
var chart1 = new AdegaChart({
data_x: [0,1,2],
data_y: [[6,10,2], [7,9,2.5]],
div_target: "myChart2",
error_y: [[1,2,3], [1.5,0.1,0.5]],
type:["bar", "scatter"],
title: "Exemplo 2",
legend: ["linha 1", "linha 2"]
data_x: grafico_qtd_cursada_aprov_x,
data_y: grafico_qtd_cursada_aprov_y,
div_target: "qtd_cursada_aprov",
type:["scatter"],
title: "Quantidade de Alunos/Vezes Cursadas",
legend: "Quantidade de alunos"
});
</script>
......
......@@ -30,7 +30,10 @@ def index(request, degree_id):
analysis_result = get_list_courses(request.session, degree)
courses_list = analysis_result["cache"]
code_to_name = analysis_result["disciplinas"]
for code in courses_list:
courses_list[code]["name"] = code_to_name[code]
return render(request, 'course/index.html', {
"courses": courses_list,
"degree": degree
......
......@@ -60,6 +60,7 @@
<span class="data">{{ degree_data.taxa_reprovacao.0|floatformat:2 }}% &#177; {{degree_data.taxa_reprovacao.1|floatformat:2 }}%</span>
</div>
</div>
<br>
<div class="panel-container">
<div class="data-panel">
<h3>Aprovação Aproveitamento de Conhecimento</h3>
......@@ -74,11 +75,14 @@
{% endcomment %}
<div id="graficos">
<div class="row">
<div class="col-md-6">
<div class="col-md-12">
<!-- <h3>Quantidade alunos/IRA</h3> -->
<div id="ira_semestral"></div>
</div>
<div class="col-md-6">
</div>
</div class="row">
<div class="col-md-12">
<!-- <h3>Quantidade evasão/período</h3> -->
<div id="evasao_semestre"></div>
</div>
......@@ -96,14 +100,13 @@
<canvas id="aluno_periodo"
data-data='{{ degree_data.aluno_periodo|safe }}'></canvas>
</div>
<br>
<div class="col-md-6">
<h3>Nota Média/Periodo</h3>
<canvas id="nota_media_periodo"
data-data='{{ degree_data.nota_media_periodo|safe }}'></canvas>
</div>
<br>
<div class="col-md-6">
<h3>Taxa Aprovação/Periodo</h3>
<canvas id="taxa_aprovacao_periodo"
......@@ -158,14 +161,20 @@
div_target: "ira_semestral",
title: "Quantidade alunos/IRA",
fill: "none",
legend: ["Ira médio", "Alunos sem evasão", "Formados"]
legend: [
"Quantidade total de alunos",
"Quantidade total de alunos sem evasão",
"Quantidade total de alunos formados"
],
});
var chart2 = new AdegaChart({
data: evasao_semestre_data,
div_target: "evasao_semestre",
title: "Quantidade evasão/período",
fill: "none",
legend: ["Taxa", "Quantidade"]
type: ["scatter","bar"],
legend: ["Taxa", "Quantidade"],
data_axis_y: ["y2","y1"]
});
......
......@@ -51,14 +51,25 @@ def admission_class_ira_per_semester(df):
for semester in semester_grouped:
student_grouped = semester[1].groupby('ID_ALUNO')
ira_class = 0
ira_class = []
# Compute all individual IRA from an class
for student in student_grouped:
#TODO: Verify if this can be calculated without groupby
ira_individual =( (student[1].MEDIA_FINAL*student[1].TOTAL_CARGA_HORARIA).sum() )/(100*student[1].TOTAL_CARGA_HORARIA.sum())
ira_class += ira_individual
ira_class = ira_class / len(student_grouped)
dict_ira_semester.update({semester[0]:ira_class})
ira_individual =(
(student[1].MEDIA_FINAL*student[1].TOTAL_CARGA_HORARIA).sum() )/(100*student[1].TOTAL_CARGA_HORARIA.sum()
)
ira_class.append(ira_individual)
# Compute the mean and standard variation from an class
# semester[0] represents a semester/year key
dict_ira_semester.update({
semester[0]: [
np.mean(ira_class),
np.std(ira_class)
]
})
dict_admission.update({admission[0]:dict_ira_semester})
return dict_admission
......
......@@ -145,20 +145,50 @@ def period_evasion_graph(df):
di_qtd = {}
dic = {}
evasions_total = 0
# Discover the minimum and maximum values for year
year_start = int(df['ANO'].min())
year_end = int(df['ANO'].max()) + 1
students = df.drop_duplicates()
# Iterate between all semester/year possible
for year in range(year_start, year_end):
for semester in range(1, 3):
evasions = students.loc[(df['ANO_EVASAO'] == str(year)) & (df['SEMESTRE_EVASAO'] == str(semester))].shape[0]
# Filter the rows and mantain only the registers
# that match with year and semester of this iteration
evasions = students.loc[
(df['ANO_EVASAO'] == str(year)) &
(df['SEMESTRE_EVASAO'] == str(semester))
]
# Count only one row per student by removing
# all duplicate rows with same MATR_ALUNO
# and keeping the first row founded
# Than, get the number of rows computed
evasions = evasions.drop_duplicates(
subset="MATR_ALUNO",
keep='first'
).shape[0]
# Name of string on dictionary generated
date = str(year) + ' {}º Período'.format(semester)
di_qtd[date] = evasions
# Count the total of evasions identified, that will be
# used to compute the rate
evasions_total += evasions
# If at least one evasion was computed
if evasions_total:
# Compute the ratio of evasion per
# semester and name the attributes
for di in di_qtd:
qtd = di_qtd[di]
dic[di] = {'qtd': qtd, 'taxa': (qtd/evasions_total)*100}
dic[di] = {'qtd': qtd, 'taxa': (float(qtd)/evasions_total)*100}
return dic
def build_dict_ira_medio(alunos):
......@@ -182,14 +212,26 @@ def build_dict_ira_medio(alunos):
return dic
def build_degree_json(path,df):
def merge_dicts(dict1, dict2, dict3):
dict_out = {}
for key, value in dict1.items():
v2 = dict2[key] if key in dict2 else None
v3 = dict3[key] if key in dict3 else None
dict_out[key] = {'ira_medio': value, 'sem_evasao': v2, 'formatura': v3}
dict_out[key] = {
'ira_medio': value,
'sem_evasao': v2,
'formatura': v3
}
return dict_out
dic = merge_dicts(average_ira_graph(df),current_students_average_ira_graph(df),graduates_average_ira_graph(df))
dic = merge_dicts(
average_ira_graph(df),
current_students_average_ira_graph(df),
graduates_average_ira_graph(df)
)
degree_json = {
"ira_medio_grafico": json.dumps(sorted(dic.items())),
"evasao_grafico": json.dumps(sorted(period_evasion_graph(df).items())),
......
......@@ -115,24 +115,48 @@ class StudentAnalysis:
return aprovacoes_semestres
def turma_ingresso(self, df=None):
df = df if df is not None else self.data_frame
df = df.drop_duplicates(subset="MATR_ALUNO", keep="first")
admissions = {}
for i,std in df.iterrows():
admissions[std["MATR_ALUNO"]] = std["ANO_INGRESSO_y"]+"/"+std["SEMESTRE_INGRESSO"]
return admissions
@memoize
def posicao_turmaIngresso_semestral(self, df=None):
df = df if df is not None else self.data_frame
grr_to_admissions = self.turma_ingresso()
iras = self.ira_semestral()
iraMax = {}
for matr in iras:
for semestreAno in iras[matr]:
if not (semestreAno in iraMax):
iraMax[semestreAno] = iras[matr][semestreAno]
else:
if (iras[matr][semestreAno] > iraMax[semestreAno]):
iraMax[semestreAno] = iras[matr][semestreAno]
for matr in iras:
for semestreAno in iras[matr]:
iras[matr][semestreAno] /= iraMax[semestreAno]
return iras
admissions = defaultdict(list)
# Create an dict of list where each key represent an admission class,
# and its values represents the set of students
# By instance: {"2015/1":["GRR20151346","GRR20154562", ...], ...}
for grr in grr_to_admissions:
admissions[grr_to_admissions[grr]].append(grr)
iras_by_semester = self.ira_semestral()
positions = defaultdict(dict)
for grr in iras_by_semester:
for semester in iras_by_semester[grr]:
student_admission = admissions[grr_to_admissions[grr]]
competition = [matr for matr in student_admission if semester in iras_by_semester[matr]]
classifications = sorted(
competition,
key = lambda matr: iras_by_semester[matr][semester]
)
positions[grr][semester] = (1+classifications.index(grr))/len(competition)
return positions
@memoize
def periodo_real(self, df=None):
......@@ -200,6 +224,7 @@ class StudentAnalysis:
return students
@memoize