diff --git a/adega/urls.py b/adega/urls.py index 5f23d84f6196e60a8321ea8149c0808899219636..ca4c12b206ab880f4cd8cf5ea83b678d10fd6655 100644 --- a/adega/urls.py +++ b/adega/urls.py @@ -18,6 +18,8 @@ urlpatterns = patterns('', url(r'^change_course/$', views.change_course, name='change_course'), url(r'^student/', include('student.urls', namespace='student')), + + url(r'^turmaIngresso/', include('turmaIngresso.urls', namespace='turmaIngresso')), url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': 'public:index'}, name='logout'), ) diff --git a/adega/utilidades/__init__.py b/adega/utilidades/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/adega/utilidades/data.py b/adega/utilidades/data.py new file mode 100644 index 0000000000000000000000000000000000000000..5777389ae77f541d1d2fa4433309b5bb44c89294 --- /dev/null +++ b/adega/utilidades/data.py @@ -0,0 +1,2 @@ +def diferenca_semestres(ano_inicio, semestre_inicio, ano_fim, semestre_fim): + return 2*(ano_fim - ano_inicio) + (semestre_fim - semestre_inicio) + 1 \ No newline at end of file diff --git a/curso/models.py b/curso/models.py index 14a6f16bbbddfcf87955fb1d8dce0ba4dd99d830..03b4cf29a54af6346e653134e5c1deb82d86b055 100644 --- a/curso/models.py +++ b/curso/models.py @@ -1,49 +1,44 @@ +#*-* coding:utf-8 from django.db import models - from disciplina.models import Disciplina +from django.db.models import Max -PERIODO_DISCIPLINA = ( - ('1', '1 Semestre'), - ('2', '2 Semestre'), - ('3', '3 Semestre'), - ('4', '4 Semestre'), - ('5', '5 Semestre'), - ('6', '6 Semestre'), - ('7', '7 Semestre'), - ('8', '8 Semestre'), - ('9', '9 Semestre'), - ('10', '10 Semestre'), -) - -SEMESTRE_INICIO = ( - ('1', '1 Semestre'), - ('2', '2 Semestre'), +TIPO_DISCIPLINA = ( + ('OB','Obrigatória'), + ('OP','Optativa'), ) class Curso(models.Model): nome = models.CharField(max_length=50) + codigo = models.CharField(max_length=4, primary_key=True) + + ano_relatorio=models.PositiveIntegerField(null=True, blank=True) + semestre_relatorio=models.PositiveIntegerField(null=True, blank=True) def __unicode__(self): - return "%s-%s" % (self.id, self.nome) + return "%s - %s" % (self.codigo, self.nome) def __str__(self): - return "%s-%s" % (self.id, self.nome) + return "%s - %s" % (self.codigo, self.nome) -class AnoGradeCurso(models.Model): +class Grade(models.Model): + #Todo: Adicionar unique ou pk no curso + ano_inicio curso = models.ForeignKey(Curso) ano_inicio = models.IntegerField() - semestre_inicio = models.CharField(max_length=1, choices=SEMESTRE_INICIO, default="1") - + disciplinas = models.ManyToManyField(Disciplina, through='DisciplinaGrade') + + def obter_qtd_periodos(self): + periodo_maximo_dict = DisciplinaGrade.objects.filter(grade=self).aggregate(Max('periodo')) + return periodo_maximo_dict['periodo__max'] def __unicode__(self): - return "%s-%s" % (self.ano_inicio, self.semestre_inicio) + return "%s - %s" % (self.anoInicio, self.semestreInicio) def __str__(self): - return "%s-%s" % (self.ano_inicio, self.semestre_inicio) + return "%s - %s" % (self.anoInicio, self.semestreInicio) class DisciplinaGrade(models.Model): - disciplina_grade = models.ForeignKey(AnoGradeCurso) - #curso_grade = models.ForeignKey(Grade) - codigo_disc = models.ForeignKey(Disciplina) - periodo = models.CharField(max_length=1, choices=PERIODO_DISCIPLINA, default="1") - + grade = models.ForeignKey(Grade) + disciplina = models.ForeignKey(Disciplina) + periodo = models.PositiveIntegerField() + tipo_disciplina = models.CharField(max_length=255) diff --git a/disciplina/models.py b/disciplina/models.py index 28c665b4736948aac9f26695c3d48d582bd6fe3a..0fe5d3bbf9d3cc34e5cca52d190dec72b935b3dd 100644 --- a/disciplina/models.py +++ b/disciplina/models.py @@ -1,12 +1,17 @@ +#-*- coding:utf-8 from django.db import models +#TODO: Descobrir se o trabalho de graduação sempre será uma matéria optativa ou obrigatória, ou então precisa ser um tipo de disciplina especÃfico + + + class Disciplina(models.Model): codigo = models.CharField(max_length=5, primary_key = True) - nome = models.CharField(max_length=100) + nome = models.CharField(max_length=255) carga_horaria = models.FloatField() def __unicode__(self): return self.nome def __str__(self): - return self.nome \ No newline at end of file + return self.nome diff --git a/load_csv/__init__.py b/load_csv/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/load_csv/fix_ementa_relatorio_disciplinas.sh b/load_csv/fix_ementa_relatorio_disciplinas.sh new file mode 100755 index 0000000000000000000000000000000000000000..faed589a0c7e3c40f6a8004674c0d6453b6dce62 --- /dev/null +++ b/load_csv/fix_ementa_relatorio_disciplinas.sh @@ -0,0 +1,21 @@ +#! /bin/bash + +FILE=$(tail -n +2 "$1") #sem cabeçalho + +head -1 $1 #cabeçalho + +while read line +do + ementa=$(echo "$line" | cut -d',' -f15) + re='^[0-9]+$' + if [[ $ementa =~ $re ]] ; then + echo "$line" | cut -d',' -f1-14 --output-delimiter="," | xargs echo -n + echo -n ",," + echo "$line" | cut -d',' -f15- --output-delimiter="," + else + echo "$line" + fi + +done <<< "$FILE" + + diff --git a/load_csv/fix_evasao_aluno.py b/load_csv/fix_evasao_aluno.py new file mode 100755 index 0000000000000000000000000000000000000000..527dd437fb12e31a0f0a2319dd9976763964ded4 --- /dev/null +++ b/load_csv/fix_evasao_aluno.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +#*-* coding:utf-8 + +from __future__ import print_function +import pandas as pd +import sys + + +def shift_evasao(row): + if row['ANO_EVASAO'] in ("1o. Semestre","2o. Semestre"): + row['PERIODO_EVASAO'] = row['ANO_EVASAO'] + row['ANO_EVASAO'] = row['DT_SAIDA'] + row['DT_SAIDA'] = None + return row + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + +PATH = sys.argv[1] +if not PATH: + eprint('Erro: Passe o caminho do relatório de matricula dos alunos como parametro') + +df = pd.read_csv(PATH) + +df = df.apply(shift_evasao,axis=1) + + +df.to_csv(sys.stdout, sep=',', encoding='utf-8') + + + diff --git a/load_csv/fix_historico_aluno.py b/load_csv/fix_historico_aluno.py new file mode 100755 index 0000000000000000000000000000000000000000..c1df693dadfda4e55103afb08f6a1897ffc193ef --- /dev/null +++ b/load_csv/fix_historico_aluno.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +#*-* coding:utf-8 + +from __future__ import print_function +import pandas as pd +import sys + +def preenhce_situacao_limpa_media_credito(row): + if row['MEDIA_CREDITO'] != 'A': + row['SITUACAO_CURRICULO'] = row['MEDIA_CREDITO'] + row['MEDIA_CREDITO']='' + return row + + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + +PATH = sys.argv[1] +if not PATH: + eprint('Erro: Passe o caminho do relatório do historico dos alunos como parametro') + +df = pd.read_csv(PATH) +#shifta DESCR_ESTRUTURA e ID_ESTRUTURA_CUR uma coluna para direita +df = df.rename(columns={'DESCR_ESTRUTURA':'OLD_DESCR_ESTRUTURA'}) +df = df.rename(columns={'ID_ESTRUTURA_CUR':'DESCR_ESTRUTURA'}) +df = df.rename(columns={'ID_NOTA':'ID_ESTRUTURA_CUR'}) +df['ID_NOTA'] = pd.Series() + +df = df.apply(preenhce_situacao_limpa_media_credito,axis=1) + +df.to_csv(sys.stdout, sep=',', encoding='utf-8') + + + diff --git a/load_csv/read_csv.py b/load_csv/read_csv.py new file mode 100755 index 0000000000000000000000000000000000000000..3b5a51b7abe403128dd899a2d0e1306267ad2bb1 --- /dev/null +++ b/load_csv/read_csv.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python +# coding: utf-8 +from __future__ import print_function +import sys +import os +import django +import pandas as pd +from sets import Set + +sys.path.append("..") +os.environ["DJANGO_SETTINGS_MODULE"] = "adega.settings" +django.setup() + +from django.db import models +from student.models import * +from curso.models import * +from turmaIngresso.models import * +from disciplina.models import * +from turma.models import * +from student.analysis import calcularIraAluno +from django.db import models +from django.core.exceptions import ObjectDoesNotExist + +DIR_RELATORIOS="../relatorios/" +CAMINHO_RELATORIO_DISCIPLINAS=DIR_RELATORIOS + "11.02.01.99.06-MUDA.csv" +CAMINHO_RELATORIO_MATRICULA=DIR_RELATORIOS + "11.02.04.99.43-MUDA.csv" +CAMINHO_RELATORIO_HISTORICOS=DIR_RELATORIOS + "11.02.05.99.33-historico-ira-curso-MUDA.csv" + + +""" +--------------------------------EXCEPTIONS-------------------------------- + +""" +class AlunoNaoExiste(Exception): + pass +class DisciplinaNaoExiste(Exception): + pass +class NenhumCursoEncontrado(Exception): + pass +class NenhumaGradeEncontrada(Exception): + pass +class GradeNaoEncontrada(Exception): + pass +class NenhumaDisciplinaEncontrada(Exception): + pass +""" +--------------------------------------------------------------------------- +""" +#printa na saÃda de erros +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + + + +""" +-------------------------FUNÇÕES LEITURA DOS RELATORIOS-------------------- +""" +#todo: tratar exceptions +def ler_relatorio_disciplinas(listaDisciplinas): + + #validar se existe codigo do curso + cursos_df=listaDisciplinas.drop_duplicates(subset=['COD_CURSO']) + + if cursos_df.empty: + raise NenhumCursoEncontrado() + + curso, curso_created = Curso.objects.get_or_create(codigo=cursos_df['COD_CURSO'][0], + nome=cursos_df['NOME_UNIDADE'][0]) + if curso_created: + #grade criada, pois não temos todas as grades e queremos manter uma relação entre as disciplinas e o curso + grade_fake = Grade(ano_inicio=1000, + curso=curso) + grade_fake.save() + + grades_df=listaDisciplinas[listaDisciplinas.COD_CURSO==curso.codigo].drop_duplicates(subset=['NUM_VERSAO']) + + if grades_df.empty: + raise NenhumaGradeEncontrada() + + for index,row in grades_df.iterrows(): + grade, grade_created = Grade.objects.get_or_create(curso=curso, + ano_inicio=row['NUM_VERSAO']) + ler_disciplinas_grade(listaDisciplinas,grade) + return curso + +def ler_disciplinas_grade(listaDisciplinas,grade): + + disciplinas_df = listaDisciplinas[(listaDisciplinas.COD_CURSO==grade.curso.codigo) + & (listaDisciplinas.NUM_VERSAO==grade.ano_inicio)].drop_duplicates(subset=['COD_DISCIPLINA']) + + if disciplinas_df.empty: + raise NenhumaDisciplinaEncontrada() + for index,row in disciplinas_df.iterrows(): + disciplina, disciplina_created = Disciplina.objects.get_or_create( + codigo=row['COD_DISCIPLINA'], defaults={ + 'nome':row['NOME_DISCIPLINA'], + 'carga_horaria':row['CH_TOTAL'], + }) + if disciplina_created: + dg = DisciplinaGrade(grade = grade, + disciplina = disciplina, + periodo = row['PERIODO_IDEAL'], + tipo_disciplina = row['TIPO_DISCIPLINA']) + dg.save() + + +def ler_relatorio_matriculas(matricula_aluno_df,curso): + #mudar NaN para None + matricula_aluno_df = matricula_aluno_df.where((pd.notnull(matricula_aluno_df)), None) + students = [] + + curso_df = matricula_aluno_df[matricula_aluno_df.COD_CURSO==curso.codigo] + + grouped_grade_df = curso_df.groupby('VERSAO') + + for ano_grade, grade_df in grouped_grade_df: + + try: + grade = curso.grade_set.get(ano_inicio=ano_grade) + except ObjectDoesNotExist as ex: + #raise GradeNaoEncontrada() + eprint("GradeNaoEncontrada: versao:{0}".format(ano_grade)) + continue; + #TODO: Remover o filtro da data de ingresso no formato certo e adicionar validação + ti_df = curso_df[matricula_aluno_df.DT_INGRESSO.str.contains("^(0[1-9]|[12][0-9]|3[01])[/](0[1-9]|1[012])[/](\d){4}$", na=False)] + + ti_df['DT_INGRESSO'] = pd.to_datetime(ti_df['DT_INGRESSO'],format="%d/%m/%Y") + ti_df['ANO'] = ti_df['DT_INGRESSO'].dt.year + ti_df['SEMESTRE'] = ti_df['DT_INGRESSO'].apply(lambda d:1 if d.month<=6 else 2) + + grouped_ti_df = ti_df.groupby(['ANO','SEMESTRE']) + + for names,group in grouped_ti_df: + ti, created_ti = TurmaIngresso.objects.get_or_create(ano=names[0], semestre=names[1], curso=curso) + for index,student_row in group.iterrows(): + #TODO: Descobrir o que fazer com evasao anual + #anual e dado invalido é tratada como primeiro semestre por enquanto + semestre_evasao = None + if student_row['PERIODO_EVASAO'] in SEMESTRE: + semestre_evasao = SEMESTRE[student_row['PERIODO_EVASAO']] + elif student_row['PERIODO_EVASAO'] == "Anual": + semestre_evasao = 1 + + student, created_student = Student.objects.get_or_create(grr=student_row['MATRICULA'][3:], + defaults={'name':student_row['ALUNO'], + 'ira':0, + 'forma_evasao':student_row['FORMA_EVASAO'], + 'ano_evasao':student_row['ANO_EVASAO'], + 'semestre_evasao': semestre_evasao, + 'turma_ingresso':ti, + 'grade_atual': grade + }) + students.append(student) + return students + + +def ler_historico_aluno(historico_df, curso): + disciplinas_gdf = historico_df.groupby('COD_ATIV_CURRIC') + grade_fake = curso.grade_set.get(ano_inicio=1000) + + #inicializa data relatorio com nulo, para ser preenchida durante a leitura + curso.ano_relatorio = None + curso.semestre_evasao = None + + + for codigo_disciplina, disciplina_df in disciplinas_gdf: + try: + disciplina = Disciplina.objects.get(codigo=codigo_disciplina) + except ObjectDoesNotExist: + primeira_linha = disciplina_df.iloc[0] + descr_estrutura = primeira_linha['DESCR_ESTRUTURA'] + carga_horaria = primeira_linha['CH_TOTAL'] + nome_disciplina = primeira_linha['NOME_ATIV_CURRIC'] + + if descr_estrutura == "Disciplinas de outros cursos": + disciplina = Disciplina(codigo=codigo_disciplina, + nome=nome_disciplina, + carga_horaria=carga_horaria) + disciplina.save() + + #FIXME: REMOVER GRADE FAKE DEPOIS DE OBTER DE TER CERTEZA QUE TEMOS TODOS OS CURRICULOS + elif descr_estrutura == "Discipl. de outros currÃculos do curso": + disciplina = Disciplina(codigo=codigo_disciplina, + nome=nome_disciplina, + carga_horaria=carga_horaria) + disciplina.save() + + dg = DisciplinaGrade(grade=grade_fake, + disciplina=disciplina, + periodo=1, + tipo_disciplina = descr_estrutura) + dg.save() + + #TODO:O que fazer com as atividades formativas? Elas não são disciplinas, porém o que fazer com elas? obs: existem poucos registros delas no relatório, talvez elas não devam contar nele mesmo + elif descr_estrutura == "Atividades Formativas Complementares": + eprint("Atividade Formativa Encontrada: codigo={0}".format(codigo_disciplina)) + continue + else: + try: + raise DisciplinaNaoExiste("DisciplinaNaoExiste: codigo={0}".format(codigo_disciplina)) + except DisciplinaNaoExiste as ex: + eprint(ex) + continue + + + #FIXME: Adicionar mais uma chave no group by para separar as turmas do mesmo ano e semestre(talvez sigla?) + turmas_gdf=disciplina_df.groupby(['ANO','PERIODO']) + #periodo do relatório na verdade é o semestre(1 ou 2) + for ano_periodo, turma_df in turmas_gdf: + if ano_periodo[1] not in SEMESTRE: + eprint("PERIODO INVALIDO: cod_disciplina={0} ano={1} periodo:{2}".format(codigo_disciplina,ano_periodo[0],ano_periodo[1])) + continue + ano = ano_periodo[0] + semestre = SEMESTRE[ano_periodo[1]] + """" + Pega a maior data do historico para ser a data que as informações do relatório são válidas + """ + if ano > curso.ano_relatorio: + curso.ano_relatorio = ano + curso.semestre_relatorio = semestre + elif ano == curso.semestre_relatorio: + if semestre > curso.semestre_relatorio: + curso.semestre_relatorio = semestre + + + turma, created_turma = Turma.objects.get_or_create(disciplina = disciplina, + ano = ano, + semestre = semestre + ) + #TODO: validar se existe estudantes iguais na turma + for index, student_row in turma_df.iterrows(): + grr=student_row['MATR_ALUNO'][3:] + try: + student = Student.objects.get(grr=grr) + except ObjectDoesNotExist: + try: + raise AlunoNaoExiste("AlunoNaoExiste: GRR={0}".format(grr)) + except AlunoNaoExiste as ex: + eprint(ex) + continue + + #TODO: Verficar se o valor 9999 é o valor real do relatório, ou foi atribuido na criptografia do relatório + nota = 0 if student_row['MEDIA_FINAL'] == 9999 else student_row['MEDIA_FINAL'] + + #Não adianta só checar o created_turma p/ saber se precisa ou não criar o aluno_turma, pois o relatório anteriormente enviado pode não conter o aluno ainda, por exemplo antes do reajuste de matricula + aluno_turma, create_aluno_turma = AlunoTurma.objects.get_or_create( + turma = turma, student = student, defaults={ + 'nota': nota, + 'situacao': student_row['SITUACAO'] + }) + #salva ano, semestre do relatório + curso.save() + + +""" +-------------------------------------------------------------------------------- +""" +def gerar(): + + #ofertaDisciplina = pd.read_csv(DIR_RELATORIOS + "11.02.03.99.05-MUDA.csv") + + + relatorio_disciplinas_df = pd.read_csv(CAMINHO_RELATORIO_DISCIPLINAS) + print("ANALISANDO O RELATÓRIO DE DISCIPLINAS") + curso = ler_relatorio_disciplinas(relatorio_disciplinas_df) + + print("ANALISANDO O RELATÓRIO DE MATRICULAS") + relatorio_matriculas_df = pd.read_csv(CAMINHO_RELATORIO_MATRICULA) + students = ler_relatorio_matriculas(relatorio_matriculas_df, curso) + + print("ANALISANDO O RELATÓRIO DOS HISTÓRICOS") + relatorio_historicos_df = pd.read_csv(CAMINHO_RELATORIO_HISTORICOS) + ler_historico_aluno(relatorio_historicos_df, curso) + + print("CALCULANDO IRAs") + # Atribui ira para os alunos dos relatórios + for student in students: + student.ira = calcular_ira(student.alunoturma_set.all()) + student.save() + + + +def apagar(): + AlunoTurma.objects.all().delete() + Student.objects.all().delete() + Turma.objects.all().delete() + Disciplina.objects.all().delete() + TurmaIngresso.objects.all().delete() + Curso.objects.all().delete() + +gerar() +#apagar() + diff --git a/middleware.py b/middleware.py new file mode 100644 index 0000000000000000000000000000000000000000..3392dc2db9c51e80a98d3ad52c06350579fdcc02 --- /dev/null +++ b/middleware.py @@ -0,0 +1,12 @@ + + + +class CourseMiddleware: + + def process_view(self, request, view_func, view_args, view_kwargs): + if request.user.is_authenticated(): + + request.session['user_courses'] = request.user.systemuser.load_courses() + + if request.session.get('course', None) is None: + request.session['course'] = request.session['user_courses'][0] diff --git a/models.py b/models.py new file mode 100644 index 0000000000000000000000000000000000000000..c969f5beddbdc26753a03acac0fa83ad7d00a31b --- /dev/null +++ b/models.py @@ -0,0 +1,49 @@ +# coding: utf-8 + +from django.contrib.auth.models import User +from django.db.models.signals import post_save +from django.db import models + +from data.models.models import Course + + +class SystemUser(models.Model): + user = models.OneToOneField(User) + + coursesTeaches = models.ManyToManyField(Course, related_name="coursesTeaches") + + courseCoordinates = models.ManyToManyField(Course, related_name="coursesCoordinates") + + def load_courses(self): + if self.user.is_staff: + courses = [x for x in Course.objects.all()] + else: + courses = [] + courses.extend(self.courseCoordinates.all()) + courses.extend(self.coursesTeaches.all()) + + return courses + + def has_course(self, course): + if self.user.is_staff: + return True + + if course in self.courseCoordinates.all() or course in self.coursesTeaches.all(): + return True + + return False + + def __unicode__(self): # pragma: no cover + return "SystemUser: %s <%s>" % (self.user.name, self.user.email) + + +# +# Cria o :model:SystemUser correspondente ao :model:User toda vez que +# um usuário é criado +# +def create_user_systemuser(sender, instance, created, **kwargs): + if created: + profile, created = SystemUser.objects.get_or_create(user=instance) + + +post_save.connect(create_user_systemuser, sender=User) \ No newline at end of file diff --git a/settings.py b/settings.py new file mode 100644 index 0000000000000000000000000000000000000000..9155c6d37aa4331fe73e0de6bd540e9503f6e7d3 --- /dev/null +++ b/settings.py @@ -0,0 +1,151 @@ +""" +Django settings for hidra project. + +For more information on this file, see +https://docs.djangoproject.com/en/1.7/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/1.7/ref/settings/ +""" + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +import os +from django.contrib.messages import constants as messages + +BASE_DIR = os.path.dirname(os.path.dirname(__file__)) + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'wqjkr2xxk^pd@bbpd)ppkr5$8o61u%+mpv)#^nz-_(&+@#6&3e' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +TEMPLATE_DEBUG = True + +ALLOWED_HOSTS = ['*'] + + +# Application definition + +INSTALLED_APPS = ( + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'adega', + 'public', + 'bootstrap3', + 'features', + 'widget_tweaks', + 'data', + 'student', + 'turmaIngresso', + 'curso', + 'disciplina', + 'turma', + 'django_extensions' +) + +MIDDLEWARE_CLASSES = ( + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', + 'adega.middleware.CourseMiddleware', +) + + +#TEMPLATE_CONTEXT_PROCESSORS = ( +# 'django.contrib.messages.context_processors.messages', +# 'django.contrib.auth.context_processors.auth' +#) + + +ROOT_URLCONF = 'adega.urls' + +WSGI_APPLICATION = 'adega.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/1.7/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), + } +} + + +TEMPLATE_CONTEXT_PROCESSORS = ( + "django.contrib.auth.context_processors.auth", + "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "django.core.context_processors.static", + "django.core.context_processors.tz", + "django.contrib.messages.context_processors.messages", + "django.core.context_processors.request", +) + +SESSION_SERIALIZER = "django.contrib.sessions.serializers.PickleSerializer" + + + +EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' +EMAIL_FILE_PATH = BASE_DIR + "/debug_mail" + + + + +LOGIN_URL = 'public:index' + +LOGIN_REDIRECT_URL = 'index' + + +MAIN_URL = 'index' + + + +SYSTEM_MAIL = "pet@inf.ufpr.br" + + + + + +# Internationalization +# https://docs.djangoproject.com/en/1.7/topics/i18n/ + +LANGUAGE_CODE = 'pt-br' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_L10N = True + +USE_TZ = False + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.7/howto/static-files/ + +STATIC_URL = '/static/' +STATIC_ROOT = 'static/' + + +MESSAGE_TAGS = { + messages.ERROR: '', + 40: 'danger', +} + +#from data.settings import * diff --git a/student/analysis.py b/student/analysis.py index edc1fdd4bc9089825a517702913a2e2ead2f9c2b..18599adeb7ff85e84e568ab3d2d7bc1d2edcd1b7 100644 --- a/student/analysis.py +++ b/student/analysis.py @@ -1,128 +1,185 @@ +#*-* coding:utf-8 from __future__ import division from student.models import Student from turma.models import * from turmaIngresso.models import TurmaIngresso from curso.models import * from datetime import datetime - - -#TODO: descobrir se ignora ou nao as outras situacoes do aluno -#DUVIDA: Aprovacao por prova, conta no indice de reprovacao -def calcularIndiceReprovacao(alunoTurmas): - qtd_reprovacao = 0 - qtd_materias = 0 +from django.db.models import Max +from adega.utilidades.data import diferenca_semestres + +SITUACOES_CURSADAS_ATE_O_FIM = ( + u'Reprovado por nota', + u'Aprovado', + u'Aprovado Adiantamento', + u'Reprovado por Frequência', + u'Reprovado sem nota', +) +SITUACOES_DISCIPLINA_CONCLUIDA =( + u'Aprovado', + u'Aprovado Adiantamento', + u'Equivalência de Disciplina', + u'Aprovado Conhecimento', + u'Dispensa de Disciplinas (com nota)', + u'Dispensa de Disciplinas (sem nota))' +) +SITUACOES_APROVACAO=( + u'Aprovado', + u'Aprovado Adiantamento', +) + +SITUACOES_CONTRIBUEM_IRA = ( + u'Reprovado por nota', + u'Aprovado', + u'Reprovado por Frequência', + u'Reprovado sem nota', + u'Aprovado Adiantamento' # Realmente contribui? +) + + +#TODO: descobrir se ignora ou nao as outras situacoes do aluno(ADIANTAMENTO? APROVEITAMENTO?EQUIVALENCIA?) +def calcular_indice_aprovacao(alunoTurmas): + """Calcula o Ãndice de aprovação dado uma lista de relações entre alunos e turmas. Retorna um decimal entre 0 ou 1, caso não tenham turmas que o aluno cursou até o fim retorna None""" + qtd_aprovacoes = 0 + qtd_disciplinas = 0 for alunoTurma in alunoTurmas: - if alunoTurma.situacao == 'R': - qtd_reprovacao+=1 - qtd_materias+=1 - elif alunoTurma.situacao == 'A' or alunoTurma.situacao == 'X': - qtd_materias+=1 - return 0 if qtd_materias == 0 else qtd_reprovacao/qtd_materias - + if alunoTurma.situacao in SITUACOES_CURSADAS_ATE_O_FIM: + qtd_disciplinas+=1 + if alunoTurma.situacao in SITUACOES_APROVACAO: + qtd_aprovacoes+=1 + + return None if qtd_disciplinas == 0 else qtd_aprovacoes/qtd_disciplinas + + +def calcular_indice_aprovacao_semestral(student): + """Dado um aluno, retorna uma lista de Ãndices de aprovação ao longo dos semestres do aluno no curso. Se o aluno não concluiu nenhuma disciplina até o fim em um semestre o Ãndice deste não contará na lista""" + indices = {} + + qtd_semestres = student.obter_tempo_no_curso() + ano = student.turma_ingresso.ano + semestre = student.turma_ingresso.semestre + + for i in range(0, qtd_semestres): + aluno_turmas_semestre = student.alunoturma_set.filter(turma__ano=ano, turma__semestre=semestre) + indice_semestre = calcular_indice_aprovacao(aluno_turmas_semestre) + + if indice_semestre is not None: + chave = "{0}/{1}".format(ano, semestre) + indices[chave] = indice_semestre + + semestre = (semestre % 2) + 1 + ano+= semestre % 2 + + return indices + + + +def obter_aluno_turmas_cursadas_ate_o_fim(alunoTurmas): + """ Dada uma lista de realações entre alunos e turmas, retorna uma lista com as relações entre aluno e turmas que foram cursadas até o fim""" + aluno_turmas_cursadas_ate_o_fim = [] + -def calcularIndiceReprovacaoPorSemestre(alunoTurmas): - semestres = {} for alunoTurma in alunoTurmas: - i = str(alunoTurma.turma.ano)+"/"+str(alunoTurma.turma.semestre) - if i not in semestres: - semestres[i] = {'qtd_materias':0,'qtd_reprovacao':0} - if alunoTurma.situacao == 'R': - semestres[i]['qtd_reprovacao']+=1 - semestres[i]['qtd_materias']+=1 - elif alunoTurma.situacao == 'A' or alunoTurma.situacao == 'X': - semestres[i]['qtd_materias']+=1 - - newSemestres = semestres.copy() - for key in semestres: - if semestres[key]['qtd_materias'] == 0: - newSemestres.pop(key,None) - else: - newSemestres[key]['indice'] = semestres[key]['qtd_reprovacao']/semestres[key]['qtd_materias'] - newSemestres[key]['qtd_materias'] = semestres[key]['qtd_materias'] - return newSemestres - - -def calcularIraAluno(alunoTurmas): -#I.R.A.=Somatorio (nota x c.h. da disciplina cadastrada no Historico Escolar do aluno)/carga horaria total cadastrada no Historico Escolar do aluno -#Disciplinas inseridas no historico por intermedio de aproveitamento, tanto interno como externo, nao farao parte do calculo do IRA Individual + if alunoTurma.situacao in SITUACOES_CURSADAS_ATE_O_FIM: + aluno_turmas_cursadas_ate_o_fim.append(alunoTurma) + return aluno_turmas_cursadas_ate_o_fim + + + + +def obter_qtd_disciplinas_cursadas_ate_o_fim_por_semestre(student): + """ Retorna uma lista com a quantidade de disciplinas que o aluno cursou até o fim em cada semestre que ele esteve no curso""" + quantidades= {} + + qtd_semestres = student.obter_tempo_no_curso() + ano = student.turma_ingresso.ano + semestre = student.turma_ingresso.semestre + + for i in range(0, qtd_semestres): + aluno_turmas_semestre = student.alunoturma_set.filter(turma__ano=ano, turma__semestre=semestre) + chave = "{0}/{1}".format(ano, semestre) + quantidades[chave] = len(obter_aluno_turmas_cursadas_ate_o_fim(aluno_turmas_semestre)) + semestre = (semestre % 2) + 1 + ano+= semestre % 2 + + return quantidades + + +#I.R.A.= Somatorio (nota x c.h. da disciplina cadastrada no Historico Escolar do aluno)/carga horaria total cadastrada no Historico Escolar do aluno +#TODO: Descobrir se aproveitamento e equvalência contam no ira +#TODO: Ter certeza que disciplinas eletivas contam no I.R.A., se não contribuir precisava verificar se a disiplina está na grade atual do aluno +#TODO: Descobrir qual nota considerar quando o aluno for na reprovado sem nota, usar nota preenchida na conversão do relatório +def calcular_ira(alunoTurmas): + """ Calcula o IRA de acordo com as relações de alunos com turmas. Retorna um decimal entre 0 e 1, caso tenham apenas relações onde a situação não contribuem com o ira(ex:Cancelado) nenhumma relação, retorna None """ + ira=0 carga_horaria_total = 0 for alunoTurma in alunoTurmas: - if alunoTurma.situacao == 'A' or alunoTurma.situacao == 'R': + if alunoTurma.situacao in SITUACOES_CONTRIBUEM_IRA: carga_horaria = alunoTurma.turma.disciplina.carga_horaria - carga_horaria_total+= carga_horaria - ira+=alunoTurma.nota*carga_horaria + carga_horaria_total += carga_horaria + ira += alunoTurma.nota*carga_horaria - ira/= 1 if carga_horaria_total== 0 else carga_horaria_total + if carga_horaria_total == 0 : + return None + else: + ira/=carga_horaria_total + #para ficar decimal e deixar no padrao da universidade return ira/100 -def obterIraPorSemestre(alunoTurmas): - semestres = {} - for alunoTurma in alunoTurmas: - if alunoTurma.situacao == 'A' or alunoTurma.situacao == 'R': - indice = str(alunoTurma.turma.ano)+"/"+str(alunoTurma.turma.semestre) - carga_horaria = alunoTurma.turma.disciplina.carga_horaria - if indice not in semestres: - semestres[indice] = {'ira':0,'carga_horaria_total':0} - semestres[indice]['ira']+=alunoTurma.nota*carga_horaria - semestres[indice]['carga_horaria_total']+=carga_horaria - for key in semestres: - semestres[key] = semestres[key]['ira']/semestres[key]['carga_horaria_total']/100 - return semestres +def obter_ira_por_semestre(student): + """" Retorna uma lista com o IRA do aluno em cada semestre(independente dos anteriores), caso o aluno não possa IRA em um semestre o semestre não adicionado na lista""" + iras = {} + + qtd_semestres = student.obter_tempo_no_curso() + ano = student.turma_ingresso.ano + semestre = student.turma_ingresso.semestre + + for i in range(0, qtd_semestres): + aluno_turmas_semestre = student.alunoturma_set.filter(turma__ano=ano, turma__semestre=semestre) + ira_semestre = calcular_ira(aluno_turmas_semestre) + + if ira_semestre is not None: + chave = "{0}/{1}".format(ano, semestre) + iras[chave] = ira_semestre + + semestre = (semestre % 2) + 1 + ano+= semestre % 2 + + return iras -#retorna uma lista com o ira do aluno em cada semestre, contando apenas a disciplinas feitas naquele semestre(nao a evolucao do ira) -def obterEvolucaIraPorSemetre(student): - #I.R.A.=Somatorio (nota x c.h. da disciplina cadastrada no Historico Escolar do aluno)/carga horaria total cadastrada no Historico Escolar do aluno - pass -def iraQuantidadeDisciplinas(alunoTurmas): - iraSemestre = obterIraPorSemestre(alunoTurmas) - quantidadeDisc = {} - - for alunoTurma in alunoTurmas: - if alunoTurma.situacao == 'A' or alunoTurma.situacao == 'R': - indice = str(alunoTurma.turma.ano)+"/"+str(alunoTurma.turma.semestre) - if indice not in quantidadeDisc: - quantidadeDisc[indice] = 0 - quantidadeDisc[indice] += 1 - - quantidadeDiscSemestre = {} - - for key in iraSemestre: - if key not in quantidadeDiscSemestre: - quantidadeDiscSemestre[key] = {'ira':0, 'disc':0} - quantidadeDiscSemestre[key]['ira'] = iraSemestre[key] - quantidadeDiscSemestre[key]['disc'] = quantidadeDisc[key] - - return quantidadeDiscSemestre - -def obterPoisicaoAlunoTurmaIngresso(student): - iras_student = obterIraPorSemestre(student.alunoturma_set.all()) - students = Student.objects.filter(turma_ingresso=student.turma_ingresso).exclude(pk=student.pk) - iras = [] +def obter_poisicao_aluno_turma_ingresso(student): + """" Retorna uma lista com a posição do aluno em relação a turma de ingresso ao longo dos semestres dele no curso. A posição é porcentual, então é de acordo com os alunos da turma de ingresso que possuem IRA em cada semestre. Cada Ãndice da lista é um número entre 0 e 1, caso o aluno não possua IRA em um semestre não será adicionado a lista""" + iras_student = obter_ira_por_semestre(student) posicoes = {} - for s in students: - iras.append(obterIraPorSemestre(s.alunoturma_set.all())) - - for semestre in iras_student: - qtd_alunos = 1 - pos=1 - for i in iras: - if semestre in i: - qtd_alunos+=1 - if i[semestre] > iras_student[semestre]: - pos+=1 - posicoes[semestre] = pos/qtd_alunos + posicoes = posicoes.fromkeys(iras_student.keys()) + posicoes = {semestre: {'posicao':1,'qtd_alunos':1} for semestre, valor in iras_student.items()} + + students_turma_ingresso = Student.objects.filter(turma_ingresso=student.turma_ingresso).exclude(pk=student.grr) + + for s in students_turma_ingresso: + iras = obter_ira_por_semestre(s) + for key in posicoes: + if key in iras and iras[key] is not None: + if iras[key] > iras_student[key]: + posicoes[key]['posicao']+=1 + + posicoes[key]['qtd_alunos']+=1 + + posicoes = {semestre: valor['posicao']/valor['qtd_alunos'] for semestre, valor in posicoes.items()} return posicoes + + def obterIraPorDisciplinasSemestrais (student, alunoTurmas): ira_semestres = obterIraSemestre(alunoTurmas) @@ -130,176 +187,64 @@ def obterIraPorDisciplinasSemestrais (student, alunoTurmas): datas_disciplinas = Turma.objects.filter(pk = disciplinas_cursadas.turma.pk).all() -#AnoGradeCurso, DisciplinaGrade -def calculaPeriodoReal(student, alunoTurmas): #Periodo o qual o aluno foi aprovado em todas as disciplinas anteriores a ele - turma_Ingresso = TurmaIngresso.objects.filter(pk = student.turma_ingresso.pk).all() #Obtem a turma de ingresso do aluno - - id_curso = turma_Ingresso.values('id_curso') #Retorna uma lista de dicionarios com o valor de id_curso - id_curso = id_curso[0] #Como existe apenas um id_curso, pega o primeiro elemento do dicionario que e um dicionario - curso = Curso.objects.filter(pk = id_curso['id_curso']) #Obtem o valores correspondente a id_curso no dicionario - - curso = curso.values('pk') - curso = curso[0] - - semestres = duracaoCurso(id_curso['id_curso']) - - periodos = 0 - - grade = AnoGradeCurso.objects.filter(pk = curso['pk']).all() - - grade = grade.values('pk') - grade = grade[0] - grade = grade['pk'] - disc_grade = DisciplinaGrade.objects.filter(disciplina_grade = grade).all() - for p in range(1, int(semestres)): - disc_periodo = disc_grade.filter(periodo = p) - disc_periodo = disc_periodo.values() - quantidadeDisciplinas = disc_periodo.all().count() - disciplinasConcluidas = 0 - for disc in range(1, quantidadeDisciplinas): - disc_periodo = disc_grade.filter(periodo = p) - disc_periodo = disc_periodo.values() - disc_periodo = disc_periodo[disc] - disc_periodo = disc_periodo['codigo_disc_id'] - for alunoTurma in alunoTurmas: - disciplina = alunoTurma.turma.disciplina.codigo - if disciplina == disc_periodo: - if alunoTurma.situacao == 'A' or alunoTurma.situacao == 'X': - disciplinasConcluidas += 1 - if (disciplinasConcluidas < quantidadeDisciplinas): - return periodos - periodos = periodos + 1 +def calcular_periodo_real(student, ultimo_periodo=None): + """ Retorna o último perÃodo que o aluno concluiu todas as disciplinas dos perÃodos anteriores a ele. Caso o aluno já tenha conclÃdo todas as disciplinas até o ultimo_periodo, retorna None. Se o ultimo_periodo não for informado é adotado o último perÃodo da grade atual do aluno.""" + + #Caso não seja passado o ultimo periodo, pega-se o último da grade do aluno + if ultimo_periodo is None: + ultimo_periodo = student.grade_atual.obter_qtd_periodos() + periodo_real = 0 - return periodos + completou_periodo = True + while completou_periodo and periodo_real <= ultimo_periodo: + periodo_real+=1 + disciplinas_periodo = student.grade_atual.disciplinas.filter(disciplinagrade__periodo=periodo_real) + disciplinas_aprovado_periodo = disciplinas_periodo.filter(turma__alunoturma__student=student, + turma__alunoturma__situacao__in=SITUACOES_DISCIPLINA_CONCLUIDA) + completou_periodo = len(disciplinas_aprovado_periodo) == len(disciplinas_periodo) + + if periodo_real > ultimo_periodo: + return None + return periodo_real -def duracaoCurso(curso): #Obtem a quantidade de periodo que o curso possui - grade = AnoGradeCurso.objects.filter(curso_id = curso) #Obtem a qual grade sera utilizada - id_grade = grade.values('id') #Retorna uma lista de dicionarios com dados do id - #Como existe apenas um valor para o id, obtem o primeiro valor - id_grade = id_grade[0] #O primeiro elemente do lista e um dicionario +def calcular_periodo_pretendido(student): - discGrade = DisciplinaGrade.objects.filter(disciplina_grade = id_grade['id']) #Pega a grade referente a algum ano + """Retorna o perÃodo que o aluno deveria estar caso seguisse a periodização ideal da grade dele. Caso ele já devesse ter se formado retorna None""" + #TODO: descontar semestres trancados + #TODO: considerar mudança de grade(?) + + qtd_semestres_aluno = student.obter_tempo_no_curso() + ultimo_periodo_grade = student.grade_atual.obter_qtd_periodos() + + if qtd_semestres_aluno > ultimo_periodo_grade: + return None + return qtd_semestres_aluno + - periodo = 0 +def calcular_tempo_ate_periodo(student, periodo): + """ Retorna o número de semestres que o aluno demorou para concluir todas as disciplinas até o o perÃodo informado, caso ele não tenha concluido retorna None""" + periodo_real_limitado = calcular_periodo_real(student, periodo) + + if periodo_real_limitado not in (periodo, None): + return None + + maior_ano = student.turmas.filter(disciplina__disciplinagrade__periodo=periodo-1, + alunoturma__situacao__in=SITUACOES_DISCIPLINA_CONCLUIDA).aggregate(Max('ano'))['ano__max'] + semestre_maior_ano = student.turmas.filter(disciplina__disciplinagrade__periodo=periodo-1, + alunoturma__situacao__in=SITUACOES_DISCIPLINA_CONCLUIDA, + + ano=maior_ano).aggregate(Max('semestre'))['semestre__max'] + + return diferenca_semestres(student.turma_ingresso.ano, student.turma_ingresso.semestre, maior_ano, semestre_maior_ano) + + - for disciplinas in discGrade: #Percorre todas as disciplinas da grade - if disciplinas.periodo > periodo: #Se o periodo da disciplina atual for maior que o anterior, atualiza valor do periodo - periodo = disciplinas.periodo - - return periodo #Retorna o valor do periodo, que deve ser a quantidade de periodos existentes - - -def calculaPeriodoPretendido(student): #Quantidade de periodos que aconteceram desde a entrada do aluno no curso - turma_Ingresso = TurmaIngresso.objects.filter(pk = student.turma_ingresso.pk).all() #Obtem os dados da turma de ingresso do aluno - - ano_turmaIngresso = turma_Ingresso.values('ano') #Retorna uma lista de dicionarios com dados do ano - #Como existe apenas um valor para o ano de ingresso, obtem o primeiro valor - ano_turmaIngresso = ano_turmaIngresso[0] #O primeiro elemento da lista e um dicionario - ano_turmaIngresso = ano_turmaIngresso['ano'] #Obtem o valor correspondente ao ano no dicionario - - semestre_turmaIngresso = turma_Ingresso.values('semestre') #Retorna uma lista de dicionarios com dados do semestre - #Como existe apenas um valor para o semestre de ingresso, obtem o primeiro valor - semestre_turmaIngresso = semestre_turmaIngresso[0] #O primeiro elemento da lista e um dicionari - semestre_turmaIngresso = semestre_turmaIngresso['semestre'] #Obtem o valor correspondente ao semestre no dicionario - if (student.actual_state == 'c' or student.data_egresso == ''): - data_atual = datetime.now() #Funcao para obter a data atual - mes_atual = data_atual.month #Da data atual, retira-se apenas o mes - ano_atual = data_atual.year #Da data atual, retira-se apenas o ano - if mes_atual >= 7: #Se tiver passado da metade do ano, adiciona um periodo - periodoPretendido = ((int(ano_atual) - int(ano_turmaIngresso)) * 2) + 2 - else: #Caso nao tenha chegado a metade do ano, conta apenas os anos cursados - periodoPretendido = ((int(ano_atual) - int(ano_turmaIngresso)) * 2) + 1 - - if semestre_turmaIngresso == '2': #Se o aluno ingressou no segundo semestre retira-se um periodo - periodoPretendido = int(periodoPretendido) - 1 - return int(periodoPretendido) - - else: - anoEgresso = int(student.data_egresso[:-2]) - semestreEgresso = int(student.data_egresso[-1]) - - if semestreEgresso == 2: - periodoPretendido = ((anoEgresso - int(ano_turmaIngresso)) * 2) + 1 - else: - periodoPretendido = ((anoEgresso - int(ano_turmaIngresso)) * 2) - - return periodoPretendido - -def calculaTempoAtePeriodo(student, alunoTurmas, periodoBuscado): - - periodoReal = calculaPeriodoReal(student, alunoTurmas) - - if (periodoReal == 0): - return "O aluno nao conclui nenhum periodo" - - elif (periodoBuscado > periodoReal): - return "Concluiu ate o %d periodo" % periodoReal - - else: - turma_Ingresso = TurmaIngresso.objects.filter(pk = student.turma_ingresso.pk).all() #Obtem a turma de ingresso do aluno - - ano_turmaIngresso = turma_Ingresso.values('ano') #Retorna uma lista de dicionarios com dados do ano - #Como existe apenas um valor para o ano de ingresso, obtem o primeiro valor - ano_turmaIngresso = ano_turmaIngresso[0] #O primeiro elemento da lista e um dicionario - ano_turmaIngresso = ano_turmaIngresso['ano'] #Obtem o valor correspondente ao ano no dicionario - - semestre_turmaIngresso = turma_Ingresso.values('semestre') #Retorna uma lista de dicionarios com dados do semestre - #Como existe apenas um valor para o semestre de ingresso, obtem o primeiro valor - semestre_turmaIngresso = semestre_turmaIngresso[0] #O primeiro elemento da lista e um dicionari - semestre_turmaIngresso = semestre_turmaIngresso['semestre'] #Obtem o valor correspondente ao semestre no dicionario - - id_curso = turma_Ingresso.values('id_curso') #Retorna uma lista de dicionarios com o valor de id_curso - id_curso = id_curso[0] #Como existe apenas um id_curso, pega o primeiro elemento do dicionario que e um dicionario - curso = Curso.objects.filter(pk = id_curso['id_curso']) #Obtem o valores correspondente a id_curso no dicionario - - curso = curso.values('pk') - curso = curso[0] - - grade = AnoGradeCurso.objects.filter(pk = curso['pk']).all() - - anoDisc = 0 - semestreDisc = 0 - - grade = grade.values('pk') - grade = grade[0] - grade = grade['pk'] - disc_grade = DisciplinaGrade.objects.filter(disciplina_grade = grade).all() - - for p in range(1, periodoBuscado): - disc_periodo = disc_grade.filter(periodo = p) - disc_periodo = disc_periodo.values() - - quantidadeDisciplinas = disc_periodo.all().count() - - for disc in range(1, quantidadeDisciplinas): - disc_periodo = disc_grade.filter(periodo = p) - disc_periodo = disc_periodo.values() - disc_periodo = disc_periodo[disc] - disc_periodo = disc_periodo['codigo_disc_id'] - for alunoTurma in alunoTurmas: - disciplina = alunoTurma.turma.disciplina.codigo - if disciplina == disc_periodo: - if alunoTurma.situacao == 'A' or alunoTurma.situacao == 'X': - dataDisc = Turma.objects.filter(pk = alunoTurma.turma.pk).all() - dataDisc = dataDisc.values() - dataDisc = dataDisc[0] - if (anoDisc < int(dataDisc['ano'])): - anoDisc = int(dataDisc['ano']) - semestreDisc = int(dataDisc['semestre']) - - if semestreDisc == int(semestre_turmaIngresso): - tempoConcluir = ((anoDisc - int(ano_turmaIngresso)) * 2) - elif semestreDisc > int(semestre_turmaIngresso): - tempoConcluir = ((anoDisc - int(ano_turmaIngresso)) * 2) + 1 - else: - tempoConcluir = ((anoDisc - int(ano_turmaIngresso)) * 2) - 1 - - return tempoConcluir + diff --git a/student/models.py b/student/models.py index 093d34f0414c01b11977324e1c4f38aee3ba08d0..9fbcc502722e9814aa8eb6bfe478d69fe430c98c 100644 --- a/student/models.py +++ b/student/models.py @@ -1,23 +1,67 @@ +# -*- coding: utf-8 -*- from django.db import models from turmaIngresso.models import TurmaIngresso +from curso.models import Grade + from django.core.validators import MinValueValidator +from datetime import datetime + +from adega.utilidades.data import diferenca_semestres + +FORMAS_EVASAO_CHOICES=( + ('A','Abandono'), + ('CPC','Cancelamento a Pedido do Calouro'), + ('CP','Cancelamento Pedido'), + ('DE','Descumprimento Edital'), + ('D','Desistência'), + ('DV','Desistência Vestibular'), + ('F','Falecimento'), + ('FORM','Formatura'), + ('J','Jubilamento'), + ('NCV','Não Confirmação de Vaga'), + ('NV','Novo Vestibular'), + ('R','Reopção'), + ('SE','Sem evasão'), + ('TRT','Término de Registro Temporário'), + ('TE','Transferência Externa'), + ('CC','Cancelamento Convênio'), + ('CJ','Cancelamento Judicial'), + ('DP','Desistência PROVAR'), + ('REI','Reintegração') +) -ACTUAL_STATE_CHOICES=( - ('c','cursando'), - ('t','trancado'), - ('f','formado') - ) class Student(models.Model): name= models.CharField(max_length=255) - ira= models.FloatField(validators=[MinValueValidator(0)]) - actual_state= models.CharField(max_length=1, choices=ACTUAL_STATE_CHOICES, default="c") - grr= models.PositiveIntegerField(primary_key = True) - data_egresso = models.CharField(max_length=10) - + ira= models.FloatField(validators=[MinValueValidator(0)], null=True, blank=True) + forma_evasao = models.CharField(max_length=255) + grr= models.PositiveIntegerField(primary_key=True) + ano_evasao=models.PositiveIntegerField(null=True, blank=True) + semestre_evasao = models.PositiveIntegerField(null=True, blank=True) + grade_atual = models.ForeignKey(Grade) turma_ingresso = models.ForeignKey(TurmaIngresso) - + turmas = models.ManyToManyField('turma.Turma', through='turma.AlunoTurma') + + #Retorna a quantidade de semestres do aluno no curso, semestres trancados também são contados + #TODO: considerar último data presente no relatório ou data atual mesmo(?) + def obter_tempo_no_curso(self): + ano_fim = self.turma_ingresso.curso.ano_relatorio + semestre_fim = self.turma_ingresso.curso.semestre_relatorio + + + if self.ano_evasao is not None: + ano_fim = self.ano_evasao + if self.semestre_evasao is None: + semestre_fim = 2 + else: + semestre_fim = self.semestre_evasao + + ano = self.turma_ingresso.ano + semestre = self.turma_ingresso.semestre + diferenca = diferenca_semestres(ano, semestre, ano_fim, semestre_fim) + return diferenca + def __unicode__(self): return self.name diff --git a/student/static/chartsLoader.js b/student/static/chartsLoader.js index ede72f9e14c99f34322bc482b0e520e4b0b3c2a0..5c03a22a46cb8cbfaa21cf49240d79e91f590290 100644 --- a/student/static/chartsLoader.js +++ b/student/static/chartsLoader.js @@ -2,7 +2,7 @@ var ira_semestralData = { datasets: [ { - label: "Ira Por Semestre", + label: "IRA", borderColor: "rgba(151,187,205,0.9)", backgroundColor: "rgba(151,187,205,0.5)", pointBorderColor: "rgba(151,187,205,0.9)", @@ -13,18 +13,28 @@ var ira_semestralData = { ] }; -var reprovacao_semestralData = { +var aprovacao_semestralData = { datasets: [ { type: "line", - label: "Taxa Reprovação", + label: "Taxa de aprovação", borderColor: "rgba(215, 84, 0, 0.9)", backgroundColor: "rgba(215, 84, 0, 0.9)", pointBorderColor: "rgba(215, 84, 0, 0.9)", pointBackgroundColor: "rgba(215, 84, 0, 0.9)", pointHoverBackgroundColor: "rgba(215, 84, 0, 0.9)", pointHoverBorderColor: "rgba(215, 84, 0, 0.9)", - lineTension: 0, + borderCapStyle: 'butt', + borderDash: [], + borderDashOffset: 0.0, + borderJoinStyle: 'miter', + pointBorderWidth: 1, + pointHoverRadius: 5, + pointHoverBorderWidth: 2, + pointRadius: 1, + pointHitRadius: 10, + spanGaps: false, + yAxisID: "y-axis-1" }, { @@ -43,7 +53,7 @@ var reprovacao_semestralData = { var posicao_turma_ingresso_semestralData = { datasets: [ { - label: "Ãndice de Reprovação Por Semetre", + label: "Posição aluno", borderColor: "rgba(151,187,205,0.9)", backgroundColor: "rgba(151,187,205,0.5)", pointBorderColor: "rgba(151,187,205,0.9)", @@ -97,26 +107,37 @@ var ira_disciplinas_semestreData = { window.myLine = new Chart(ctx, { type: 'line', data: ira_semestralData, - responsive: true + responsive: true, + options: { + scales: { + yAxes: [{ + ticks: { + max:1, + min: 0, + + } + }] + } + } }); } - var reprovacao_semestral = function() { - var data = $("#reprovacao_semestral").data("data"); + var aprovacao_semestral = function() { + var data = $("#aprovacao_semestral").data("data"); var labels = []; var values1 = []; var values2 = []; for(var item in data) { labels.push(data[item][0]); - values1.push(data[item][1]['indice']); - values2.push(data[item][1]['qtd_materias']); + values1.push(data[item][1][0]*100); + values2.push(data[item][1][1]); } - reprovacao_semestralData.labels = labels; - reprovacao_semestralData.datasets[0].data = values1; - reprovacao_semestralData.datasets[1].data = values2; - var ctx = document.getElementById("reprovacao_semestral").getContext("2d"); + aprovacao_semestralData.labels = labels; + aprovacao_semestralData.datasets[0].data = values1; + aprovacao_semestralData.datasets[1].data = values2; + var ctx = document.getElementById("aprovacao_semestral").getContext("2d"); var LineBar = new Chart(ctx, { type: 'bar', - data: reprovacao_semestralData, + data: aprovacao_semestralData, options: { responsive: true, legendTemplate: "lol", @@ -142,7 +163,14 @@ var ira_disciplinas_semestreData = { id: "y-axis-1", gridLines: { display: false - }, + }, + ticks: { + max:100, + min: 0, + + } + + }, { type: "linear", display: true, @@ -178,7 +206,17 @@ var ira_disciplinas_semestreData = { type: 'line', data: posicao_turma_ingresso_semestralData, responsive: true, - legendTemplate : "lol" + options: { + scales: { + yAxes: [{ + ticks: { + max:100, + min: 0, + + } + }] + } + } }); } @@ -247,7 +285,7 @@ var ira_disciplinas_semestreData = { } ira_semestral(); - reprovacao_semestral(); + aprovacao_semestral(); posicao_turma_ingresso_semestral(); ira_disciplinas_semestre(); } diff --git a/student/templates/student/index.html b/student/templates/student/index.html index 1e171d46c0d31a3e2cdb3e7952c28dc09f2207ed..5dd83397b8674e1d5ea91ef45e1ca1fbf73010c6 100644 --- a/student/templates/student/index.html +++ b/student/templates/student/index.html @@ -21,41 +21,37 @@ <td>{{ student.turma_ingresso.ano}}/{{ student.turma_ingresso.semestre}}</td> </tr> <tr> - <td>Data egresso</td> - <td>{{ student.data_egresso| default:"Não se formou"}}</td> + <td>Estado atual</td> + <td>{{ student.forma_evasao }}</td> </tr> <tr> - <td>Estado atual</td> - <td>{{ student.get_actual_state_display }}</td> + <td>Data de evasão</td> + {% if student.forma_evasao == "Sem evasão" %} + <td>Não Possui</td> + {% else %} + <td>{{ student.ano_evasao}}/{{ student.semestre_evasao}}</td> + {% endif %} </tr> <tr> <td>IRA</td> - <td>{{ student.ira }}</td> + <td>{{ student.ira| default:"Desconhecido"}}</td> </tr> <tr> - <td>Indice de reprovação</td> - <td>{{ analysis_result.indice_reprovacao|multiply:100}}%</td> + <td>Indice de aprovação</td> + <td>{{ analysis_result.indice_aprovacao|multiply:100|default:"Desconhecido"}}{{analysis_result.indice_aprovacao|yesno:"%,,"}}</td> </tr> <tr> - <td>Periodo real</td> - {% if analysis_result.periodo_real <= 0 %} - <td> 0 </td> - {% else %} - <td>{{ analysis_result.periodo_real }}</td> - {% endif %} + <td>PerÃodo real</td> + <td>{{ analysis_result.periodo_real|default:"Formado" }}</td> </tr> <tr> - <td>Periodo pretendido</td> - <td>{{ analysis_result.periodo_pretendido }}</td> + <td>PerÃodo pretendido</td> + <td>{{ analysis_result.periodo_pretendido|default:"Formado" }}</td> </tr> <tr> - <td>Tempo para fazer todas as matérias até 2º semestre</td> - {% if analysis_result.tempo_concluir_semestre <= 0 %} - <td> 0 </td> - {% else %} - <td>{{ analysis_result.tempo_concluir_semestre }}</td> - {% endif %} + <td>Qtd. de semestre demorados para concluir todas as diciplinas anteriores ao 4º perÃodo</td> + <td>{{ analysis_result.tempo_concluir_semestre|default:"Não concluÃdo" }}</td> </tr> </table> <div id="graficos"> @@ -65,8 +61,8 @@ <canvas id="ira_semestral" data-data='{{analysis_result.ira_semestral|safe}}'></canvas> </div> <div class="col-md-6"> - <h3>Gráfico do Ãndice de reprovação/Semestre(%)</h3> - <canvas id="reprovacao_semestral" data-data='{{analysis_result.indice_reprovacao_semestral|safe}}'></canvas> + <h3>Gráfico do Ãndice de Aprovação/Semestre(%)</h3> + <canvas id="aprovacao_semestral" data-data='{{analysis_result.indice_reprovacao_semestral|safe}}'></canvas> </div> </div> <div class="row"> diff --git a/student/templatetags/math_tags.py b/student/templatetags/math_tags.py index 3e8c5b8bc96d9612bf10a7b0ad29973061e07f1b..9d4bc6b2572b658774d1f910df922e8b34ebb5ec 100644 --- a/student/templatetags/math_tags.py +++ b/student/templatetags/math_tags.py @@ -5,4 +5,6 @@ register = template.Library() @register.filter(name='multiply') def multiply(value, arg): - return value*arg \ No newline at end of file + if value is not None: + return value*arg + return None \ No newline at end of file diff --git a/student/views.py b/student/views.py index d73868a785da7a47239c5c856092f0631f7c24c3..38ebe1737fe090a2cc3ccbb442d568e217369f42 100644 --- a/student/views.py +++ b/student/views.py @@ -13,14 +13,27 @@ def index(request,grr): grr= int(grr); student = get_object_or_404(Student,pk=grr) alunoTurmas = AlunoTurma.objects.filter(student=student) + + reprovacao_semestral = calcular_indice_aprovacao_semestral(student) + qtd_disciplinas_semstral = obter_qtd_disciplinas_cursadas_ate_o_fim_por_semestre(student) + reprovacao_semestral_por_qtd_disciplinas = merge_dicts(reprovacao_semestral,qtd_disciplinas_semstral) + + analysis_result = { - 'indice_reprovacao' : calcularIndiceReprovacao(alunoTurmas), - 'ira_semestral': json.dumps(sorted(obterIraPorSemestre(alunoTurmas).items())), - 'indice_reprovacao_semestral': json.dumps(sorted(calcularIndiceReprovacaoPorSemestre(alunoTurmas).items())), - 'posicao_turmaIngresso_semestral': json.dumps(sorted(obterPoisicaoAlunoTurmaIngresso(student).items())), - 'periodo_real': calculaPeriodoReal(student, alunoTurmas), - 'periodo_pretendido': calculaPeriodoPretendido(student), - 'tempo_concluir_semestre': calculaTempoAtePeriodo(student, alunoTurmas, 2), - 'ira_por_quantidade_disciplinas': json.dumps(sorted(iraQuantidadeDisciplinas(alunoTurmas).items())), + 'indice_aprovacao' : calcular_indice_aprovacao(alunoTurmas), + 'ira_semestral': json.dumps(sorted(obter_ira_por_semestre(student).items())), + 'indice_reprovacao_semestral': json.dumps(sorted(reprovacao_semestral_por_qtd_disciplinas.items())), + 'posicao_turmaIngresso_semestral': json.dumps(sorted(obter_poisicao_aluno_turma_ingresso(student).items())), + 'periodo_real': calcular_periodo_real(student), + 'periodo_pretendido': calcular_periodo_pretendido(student), + 'tempo_concluir_semestre': calcular_tempo_ate_periodo(student, 4), + #'ira_por_quantidade_disciplinas': json.dumps(sorted(iraQuantidadeDisciplinas(alunoTurmas).items())), } return render(request, 'student/index.html',{'student':student, 'analysis_result' : analysis_result}) + +def merge_dicts(dict1, dict2): + dict_out = {} + for key, value in dict1.items(): + v2 = dict2[key] if key in dict2 else None + dict_out[key] = (value,v2) + return dict_out \ No newline at end of file diff --git a/templates/adega/index.html b/templates/adega/index.html new file mode 100644 index 0000000000000000000000000000000000000000..9d81fe42c1e76ddc88550718e3d25688be12ddf1 --- /dev/null +++ b/templates/adega/index.html @@ -0,0 +1,12 @@ +{% extends 'adega/base.html' %} + +{% block content %} + +<div class="row"> + <div class="col-md-12"> + <h2>Resumo sobre {{ request.session.course.name }}</h2> + </div> +</div> + + +{% endblock content %} diff --git a/templates/adega/navbar.html b/templates/adega/navbar.html new file mode 100644 index 0000000000000000000000000000000000000000..eb548ccabc95e4a74ef17e2b57ebecccbbc97467 --- /dev/null +++ b/templates/adega/navbar.html @@ -0,0 +1,84 @@ +<nav class="navbar navbar-default navbar-fixed-top"> + <div class="container"> + <!-- Brand and toggle get grouped for better mobile display --> + <div class="navbar-header"> + <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" + data-target="#bs-example-navbar-collapse-1"> + <span class="sr-only">Toggle navigation</span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + <span class="icon-bar"></span> + </button> + <a class="navbar-brand" href="{% url 'index' %}">ADEGA</a> + </div> + + <!-- Collect the nav links, forms, and other content for toggling --> + <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> + <ul class="nav navbar-nav"> + <li> + <a href="{% url 'data:upload_database' %}"> + <span class="glyphicon glyphicon-plus"></span> + </a> + </li> + {% comment %} + <li> + <a href="#">Histórico</a> + </li> + <li> + <a href="{% url 'list_curriculuns' %}">Grades</a> + </li> + {% endcomment %} + + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> + Matérias <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"> + <li><a href="{% url 'data:materias:index' %}">Matérias</a></li> + <li><a href="#">Optativas</a></li> + <li><a href="#">TCC's</a></li> + </ul> + </li> + + {% comment %} + <li class="dropdown"> + <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> + Periodos do aluno + <span class="caret"></span> + </a> + <ul class="dropdown-menu" role="menu"> + <li><a href="#">IRA</a></li> + <li><a href="#">periodicidade</a></li> + <li><a href="#">desistência</a></li> + <li><a href="#">desistência</a></li> + </ul> + </li> + {% endcomment %} + </ul> + + + + <ul class="nav navbar-nav navbar-right"> + {% if request.session.user_courses|length > 1 %} + <li> + <form method="POST" action="{% url "change_course" %}" id="change_course"> + {% csrf_token %} + <select class="form-control" name="id" id="change_course"> + {% for course in request.session.user_courses %} + <option value="{{ course.id }}"{% if course.id == request.session.course.id %} selected{% endif %}>{{ course.name }}</option> + {% endfor %} + </select> + <noscript> + <input type="submit" value="Mudar"> + </noscript> + </form> + </li> + {% endif %} + <li><a href="{% url 'features:index' %}">Sugestões</a></li> + <li><a href="{% url 'logout' %}">Sair</a></li> + </ul> + </div> + <!-- /.navbar-collapse --> + </div> + <!-- /.container-fluid --> +</nav> diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..11ed5f0fe71b3c528da80f3dc7fc648ae86db108 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1 @@ +__author__ = 'jose' diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000000000000000000000000000000000000..4e0372c2ddf77c0e3ddfc98db0d48d569efca284 --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,30 @@ +# coding: utf-8 + + +from django.test import TestCase + +from django.core.urlresolvers import reverse + +from adega.tests.utils import create_user + +class ModelsTest(TestCase): + def test_sytemuser(self): + u = create_user('Allan', 'allan.smith@example.com', 'allanpass', course_name='Engenharia Civil') + + self.assertTrue(hasattr(u, "systemuser")) + + self.assertTrue(hasattr(u.systemuser, "coursesTeaches")) + + +class ViewsTest(TestCase): + + def test_no_login(self): + resp = self.client.get(reverse('index')) + self.assertEqual(resp.status_code, 302) + + + + + + + diff --git a/tests/test_views.py b/tests/test_views.py new file mode 100644 index 0000000000000000000000000000000000000000..22daf93d54f8ce270235451e865f00ad1db9e546 --- /dev/null +++ b/tests/test_views.py @@ -0,0 +1,87 @@ +from django.core.urlresolvers import reverse +from django.test import TestCase, Client +from django.contrib.auth.models import User +from data.models.models import Course + +from adega.tests.utils import create_user + +class ViewsTest(TestCase): + + def setUp(self): + create_user('Allan', 'allan.smith@example.com', 'allanpass', course_name='Engenharia Civil') + + + def test_index(self): + + c = Client() + + resp = c.get(reverse('index')) + self.assertEqual(resp.status_code, 302) + + c.login(username='Allan', password='allanpass') + + resp = c.get(reverse('index')) + self.assertEqual(resp.status_code, 200) + + self.assertContains(resp, 'Resumo sobre Engenharia Civil') + + + def test_change_course(self): + + c = Client() + + c.login(username='Allan', password='allanpass') + + c.get('index') + + course = Course.objects.create(name='course_test') + + u = User.objects.get(username='Allan') + + u.systemuser.coursesTeaches.add(course) + + u.save() + + resp = c.post(reverse('change_course'), {'id': course.pk}, follow=True) + + self.assertContains(resp, 'Resumo sobre course_test') + + + def test_change_course_staff(self): + + u = User.objects.create_superuser('pet', 'pet@example.com', 'petpass') + + u.is_staff = True + + u.save() + + c = Client() + + c.login(username='pet', password='petpass') + + c.get('index') + + course = Course.objects.get(name='Engenharia Civil') + + resp = c.post(reverse('change_course'), {'id': course.pk}, follow=True) + + self.assertContains(resp, 'Resumo sobre Engenharia Civil') + + + def test_change_course_denied(self): + + c = Client() + + c.login(username='Allan', password='allanpass') + + c.get('index') + + course = Course.objects.create(name='Teste 2') + + resp = c.post(reverse('change_course'), {'id': course.pk}, follow=True) + + self.assertEqual(resp.status_code, 200) + + self.assertContains(resp, 'Resumo sobre Engenharia Civil') + + diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..564b9d452e10067d1d7ff1138dd20cf4e7a8be6a --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,27 @@ + + +from django.contrib.auth.models import User +from data.models.models import Course + +def create_user(*args, **kargs): + if 'course_name' in kargs: + course_name = kargs['course_name'] + del kargs['course_name'] + else: + course_name = 'curso_teste' + + + u = User.objects.create_user(*args, **kargs) + + try: + c = Course.objects.get(name=course_name) + except: + c = Course(name=course_name) + + c.save() + + u.systemuser.courseCoordinates.add(c) + + u.save() + + return u diff --git a/turma/models.py b/turma/models.py index 2c606534da98efff658a31d5546e3954cd4d1ffa..025f85d7e90ac9756f9b91d0846d51c4d334027f 100644 --- a/turma/models.py +++ b/turma/models.py @@ -1,3 +1,4 @@ +#*-* coding:utf-8 from __future__ import unicode_literals from django.db import models @@ -6,27 +7,36 @@ from django.db import models from student.models import Student from disciplina.models import Disciplina -PERIODO = ( - ('1', '1 Semestre'), - ('2', '2 Semestre') -) +SEMESTRE = { + '1o. Semestre':1, + '2o. Semestre':2 +} + +#Incompleto - SITUACAO INVALIDA NO RELATORIO -#Aprovado, Reprovado, Cancelada, aprov/reprov por aprov. conhecimento -#DUVIDA: Se a pessoa foi reprovada por conhecimento entra no historico? -#DUVIDA: Status==SITUACAO->? SITUACAO_ALUNO_TURMA = ( - ('A','APROVADO'), - ('R', 'REPROVADO'), - ('C', 'CANCELADA'), - ('X', 'APROVADO POR CONHECIMENTO'), - ('Y', 'REPROVADO POR CONHECIMENTO') + ('RN',u'Reprovado por nota'), + ('A',u'Aprovado'), + ('TA',u'Trancamento Administrativo'),# TODO: Descobrir quando a situação do aluno Trancamento Administrativo acontece + ('RF',u'Reprovado por Frequência'), + ('C',u'Cancelado'), + ('TT',u'Trancamento Total'), + ('TSN',u'Reprovado sem nota'), + ('ED',u'Equivalência de Disciplina'), + ('AC',u'Aprovado Conhecimento'), + ('M',u'MatrÃcula'), + ('RC',u'Reprovado Conhecimento'), + ('AA',u'Aprovado Adiantamento'), + ('H',u'Horas'),# TODO: Descobrir quando a situação do aluno Horas acontece + ('DCN',u'Dispensa de Disciplinas (com nota)'), + ('DSN',u'Dispensa de Disciplinas (sem nota))') ) - class Turma(models.Model): - ano = models.CharField(max_length=4) - semestre = models.CharField(max_length=1, choices=PERIODO, default="1") + ano = models.PositiveIntegerField() + semestre = models.PositiveIntegerField() disciplina = models.ForeignKey(Disciplina) - turma = models.CharField(max_length=1, default='A') + #TODO: Quando obtermos um relatório com letra remover o null e blank e adicionar na leitura do relatorio + letra = models.CharField(max_length=1,null=True, blank=True) def __unicode__(self): return "%s-%s(%s/%s)" % (self.id, self.disciplina.nome,self.ano,self.semestre) @@ -35,8 +45,8 @@ class Turma(models.Model): return "%s-%s(%s/%s)" % (self.id, self.disciplina.nome,self.ano,self.semestre) class AlunoTurma(models.Model): - nota = models.FloatField() - situacao = models.CharField(max_length=1, choices=SITUACAO_ALUNO_TURMA, default="1") + nota = models.FloatField(null=True,blank=True) + situacao = models.CharField(max_length=255) student = models.ForeignKey(Student) turma = models.ForeignKey(Turma) diff --git a/turmaIngresso/.~c9_invoke_87hxL.py b/turmaIngresso/.~c9_invoke_87hxL.py new file mode 100644 index 0000000000000000000000000000000000000000..c0122dcbba4b614a5edf8a6991f6b809a04b07e8 --- /dev/null +++ b/turmaIngresso/.~c9_invoke_87hxL.py @@ -0,0 +1,24 @@ +from django.contrib.auth.decorators import login_required +from django.shortcuts import render +from django.shortcuts import get_object_or_404 + +from student.models import Student +from turma.models import * +from turmaIngresso.models import TurmaIngresso + +from analysis import * +import json + +#Disciplina->Turma->AlunoTurma(nota,situacao)->Aluno->TurmaIngresso +@login_required +def index(request,id_turma_ingresso): + id_turma_ingresso=int(id_turma_ingresso) + turmaingresso=TurmaIngresso.objects.get(pk=id_turma_ingresso) + analysis_result = { + 'ira_medio' : calcular_Ira_Medio(turmaingresso), + } + return render (request, 'turmaIngresso/index.html', {'turmaingresso' : turmaingresso, 'analysis_result' : analysis_result}) + + + + diff --git a/turmaIngresso/analysis.py b/turmaIngresso/analysis.py new file mode 100644 index 0000000000000000000000000000000000000000..3d7635ca0612eedb730b9693a70c619f4460df08 --- /dev/null +++ b/turmaIngresso/analysis.py @@ -0,0 +1,67 @@ +from __future__ import division +from student.models import * +from turmaIngresso.models import TurmaIngresso +from turma.models import * +from student.analysis import * +from datetime import datetime + + +def calcular_Ira_Medio(turmaingresso): + iras = {} + + alunos = turmaingresso.student_set.all() + media=0 + n=0 + for aluno in alunos: + if aluno.ira is not None: + media+=aluno.ira + n+=1 + if n==0: + return None + else: + media/=n + return media + + +def calcular_ira_semestre(turmaingresso, ano, semestre,qtd_semestres): + alunos = turmaingresso.student_set.all() + ano_aux = turmaingresso.ano + semestre_aux = turmaingresso.semestre + media = 0 + i=0 + for aluno in alunos: + tempo = aluno.obter_tempo_no_curso() + if qtd_semestres < tempo: + qtd_semestres = tempo + aluno_turmas_semestre = aluno.alunoturma_set.filter(turma__ano=ano, turma__semestre=semestre) + ira_semestre = calcular_ira(aluno_turmas_semestre) + if ira_semestre is not None: + media += ira_semestre + i+=1 + if i == 0: + return None + else: + media/=i + return media + + +def calcular_Ira_Medio_semestral(turmaingresso): + iras = {} + medias = {} + + alunos = turmaingresso.student_set.all() + ano = turmaingresso.ano + semestre = turmaingresso.semestre + qtd_semestres = 0 + for aluno in alunos: + tempo = aluno.obter_tempo_no_curso() + if qtd_semestres < tempo: + qtd_semestres = tempo + for i in range(0, qtd_semestres-1): + chave = "{0}/{1}".format(ano,semestre) + medias[chave] = calcular_ira_semestre(turmaingresso, ano, semestre, qtd_semestres) + + semestre = (semestre % 2) + 1 + ano+= semestre % 2 + + return medias \ No newline at end of file diff --git a/turmaIngresso/models.py b/turmaIngresso/models.py index 2c5382bc089750dab450433d62adac281998c249..892de5d344a4f7679eee7423f376746eb4dbeef6 100644 --- a/turmaIngresso/models.py +++ b/turmaIngresso/models.py @@ -4,18 +4,18 @@ from django.db import models from curso.models import Curso -PERIODO_INGRESSO = ( - ('1', '1 Semestre'), - ('2', '2 Semestre') +SEMESTRE_INGRESSO = ( + (1, '1 Semestre'), + (2, '2 Semestre') ) class TurmaIngresso(models.Model): - ano = models.CharField(max_length=4) - semestre = models.CharField(max_length=1, choices=PERIODO_INGRESSO, default="1") - id_curso = models.ForeignKey(Curso) + ano = models.PositiveIntegerField() + semestre = models.PositiveIntegerField() + curso = models.ForeignKey(Curso) def __unicode__(self): - return "%s-%s; Curso:%s" % (self.ano, self.semestre, self.id_curso) + return "%s-%s" % (self.ano, self.semestre) def __str__(self): - return "%s-%s; Curso:%s" % (self.ano, self.semestre, self.id_curso) + return "%s-%s" % (self.ano, self.semestre) diff --git a/turmaIngresso/templates/turmaIngresso/index.html b/turmaIngresso/templates/turmaIngresso/index.html new file mode 100644 index 0000000000000000000000000000000000000000..1a2ce385b447e9df63a961ba3c754e404a13d72e --- /dev/null +++ b/turmaIngresso/templates/turmaIngresso/index.html @@ -0,0 +1,38 @@ +{% extends 'adega/base.html' %} + + +{% load widget_tweaks %} + +{% load math_tags %} +{% block content %} +{% load static %} +<script type="text/javascript" src="{% static 'chartsLoader.js' %}"></script> + +<div class="row"> + <div class="col-md-12"> + <h1> Informações da Turma</h1> + <table class="table"> + <tr> + <td>Ano:</td> + <td>{{turmaingresso.ano}}</td> + </tr> + <tr> + <td>semestre:</td> + <td>{{turmaingresso.semestre}}</td> + </tr> + <tr> + <td>Ira</td> + <td>{{ analysis_result.ira_medio|default:"Desconhecido" }}</td> + </tr> + </table> + </div> + <div id="graficos"> + <div class="row"> + <div class="col-md-6"> + <h3>Gráfico do IRA/Semestre</h3> + <canvas id="ira_semestral" data-data='{{analysis_result.ira_semestral|safe}}'></canvas> + </div> + </div> + </div> +</div> +{% endblock content %} \ No newline at end of file diff --git a/turmaIngresso/urls.py b/turmaIngresso/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..6ee74931f9814ca0b5f2e7f26ecf8a00968ff3bb --- /dev/null +++ b/turmaIngresso/urls.py @@ -0,0 +1,7 @@ +from django.conf.urls import patterns, url + +from turmaIngresso import views + +urlpatterns = patterns('', + url(r'^([0-9]+)/$', views.index, name='index'), +) diff --git a/turmaIngresso/views.py b/turmaIngresso/views.py index 91ea44a218fbd2f408430959283f0419c921093e..5f813b864b1a2f14127f0dcfa348c10ccc0e94fe 100644 --- a/turmaIngresso/views.py +++ b/turmaIngresso/views.py @@ -1,3 +1,21 @@ +from django.contrib.auth.decorators import login_required from django.shortcuts import render +from django.shortcuts import get_object_or_404 -# Create your views here. +from student.models import Student +from turma.models import * +from turmaIngresso.models import TurmaIngresso + +from analysis import * +import json + +#Disciplina->Turma->AlunoTurma(nota,situacao)->Aluno->TurmaIngresso +@login_required +def index(request,id_turma_ingresso): + id_turma_ingresso=int(id_turma_ingresso) + turmaingresso=TurmaIngresso.objects.get(pk=id_turma_ingresso) + analysis_result = { + 'ira_medio' : calcular_Ira_Medio(turmaingresso), + 'ira_semestral' : json.dumps(sorted(calcular_Ira_Medio_semestral(turmaingresso).items())), + } + return render (request, 'turmaIngresso/index.html', {'analysis_result' : analysis_result, 'turmaingresso' : turmaingresso}) diff --git a/urls.py b/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..b0304fb91f0d51920f36745b56cb89511212ab6e --- /dev/null +++ b/urls.py @@ -0,0 +1,25 @@ +from django.conf.urls import patterns, include, url +from django.contrib import admin + +from adega import views + + +urlpatterns = patterns('', + url(r'^$', views.index, name='index'), + + url('', include('data.urls', namespace='data')), + + url(r'^public/', include('public.urls', namespace='public')), + + url(r'^sugestoes/', include('features.urls', namespace='features')), + + url(r'^admin/', include(admin.site.urls)), + + url(r'^change_course/$', views.change_course, name='change_course'), + + url(r'^student/', include('student.urls', namespace='student')), + + url(r'^turmaIngresso/', include('turmaIngresso.urls', namespace='turmaIngresso')), + + url(r'^logout/$', 'django.contrib.auth.views.logout', {'next_page': 'public:index'}, name='logout'), +) \ No newline at end of file diff --git a/utilidades/__init__.py b/utilidades/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/utilidades/data.py b/utilidades/data.py new file mode 100644 index 0000000000000000000000000000000000000000..5777389ae77f541d1d2fa4433309b5bb44c89294 --- /dev/null +++ b/utilidades/data.py @@ -0,0 +1,2 @@ +def diferenca_semestres(ano_inicio, semestre_inicio, ano_fim, semestre_fim): + return 2*(ano_fim - ano_inicio) + (semestre_fim - semestre_inicio) + 1 \ No newline at end of file diff --git a/views.py b/views.py new file mode 100644 index 0000000000000000000000000000000000000000..925871489f4a550342eb68594c98de1cc7513695 --- /dev/null +++ b/views.py @@ -0,0 +1,29 @@ +# coding: utf-8 + +from django.shortcuts import render, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.views.decorators.http import require_POST +from django.http import HttpResponseRedirect, JsonResponse +from django.core.urlresolvers import reverse + +from data.models.models import Course, Curriculum + + +@login_required +def index(request): + return render(request, 'adega/index.html') + + + +@login_required +@require_POST +def change_course(request): + + course_id = int(request.POST.get('id', '0')) + + course = get_object_or_404(Course, pk=course_id) + + if request.user.systemuser.has_course(course): + request.session['course'] = course + + return HttpResponseRedirect(reverse("index")) \ No newline at end of file