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

Merge branch 'development' into 'master'

Merge 'development' branch into 'master'

See merge request !50
parents a10372c1 0733528c
Pipeline #19595 passed with stage
in 26 seconds
# info de contas criadas no sistema
login_info
# base de dados
*.csv
......@@ -6,13 +8,16 @@
*.sqlite3
src/cache
*.json
base_dados/*
base_dados
src/.coverage
# lixo
static/*
# lixo
src/static
**/migrations/**
*~
*.swp
......
before_script:
- export LC_ALL=C.UTF-8
- export LANG=C.UTF-8
- apt-get update -qq
- apt-get install -y make
- make install
- pip3 install -U pip setuptools pipenv==9.0.3
- pip3 --version
- pipenv install
- source $(pipenv --venv)/bin/activate
- python --version
- pip3 show django | grep Version
# - apt-get update -qq
# - apt-get install -y make
# - make install
# - make install-user
#- pip3 install -U pip setuptools pipenv==9.0.3
## pip3 --version
#- pipenv install
#- source $(pipenv --venv)/bin/activate
# - pipenv shell
# - python --version
# - pip3 show django | grep Version
django-tests:
tags:
......@@ -20,7 +19,11 @@ django-tests:
- regular
script:
- cd src
- python manage.py test
# - python manage.py makemigrations degree
# - python manage.py makemigrations educator
# - python manage.py makemigrations upload
# - python manage.py migrate
#- python manage.py test
The MIT License (MIT)
Copyright (c) 2016 Simple is Better Than Complex
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# ADEGA
Este software faz parte de um projeto do PET Computação UFPR para
Este software faz parte de um projeto do PET Computação UFPR para
análise de dados dos cursos de graduação da UFPR. Veja a [wiki](http://gitlab.c3sl.ufpr.br/adega/adega/wikis/home).
......@@ -21,9 +21,97 @@ E podem ser clonado com o comando
```bash
$ git clone git@gitlab.c3sl.ufpr.br:adega/adega.git
```
## Instalação e dependências com docker
Para executar o projeto com o docker, siga os seguintes passos:
```bash
$ git clone git@gitlab.c3sl.ufpr.br:adega/adega.git # Comando já executado
$ cd adega/
$ sudo make docker-install # Instala o docker.io e docker-compose
$ sudo apt install docker.io
$ sudo make docker-up # Executa os containers postgres e webserver
```
## Desenvolvimento com o docker
### Dependências
Docker >=1.13.1
Docker-compose >=1.21.2
#### Possíveis erros:
*1*: O docker-compose padrão nos repositórios podem não conter a versão mais recente. Caso a etapa de instalação não funcione, consulte a [referência dos desenvolvedores](https://github.com/docker/compose/releases).
## Instalação e dependências
*2*: Seu computador pode ter problemas ao configurar o DNS. Neste caso, erros parecidos como os a seguir irão acontecer:
```bash
E: Unable to locate package python3-pip
E: Unable to locate package postgresql-client
```
Neste caso, consulte [este tutorial](https://development.robinwinslow.uk/2016/06/23/fix-docker-networking-dns/) para resolver o problema.
Depois de fazer o tutorial, lembre de utilizar o comando `sudo docker system prune -a` para limpar a cache e evitar os problemas.
### Uso
Enquanto o `sudo make docker-up` estiver sendo executado, as alterações feitas nos arquivos do projeto serão compartilhadas com os arquivos do container docker. Ou seja, é possível alterar qualquer arquivo do projeto e haverá resultados em tempo real.
Assim como é possível realizar qualquer comando como seria feito no com o manage.py, também é possível por meio do comando `sudo make docker-manage`. Por exemplo:
```bash
$ sudo make docker-manage makemigrations uploads
$ sudo make docker-manage migrate
$ sudo make docker-manage createsuperuser
```
Para realizar esses comandos, certifique-se que o comando `make docker-up`está em execução (recomenda-se deixar uma aba no terminal para isso).
## Recomendações para o docker
É recomendado que o usuário configure o docker para que o mesmo possa ser executado sem necessidades de privilégios de superusuário, assim não haverá necessidade do uso de `sudo`. Caso contrário, os comandos realizados com `sudo make` poderão criar arquivos cujo proprietário é o usuário `root`. Caso a recomendação não for seguida, o seguinte comando irá alterar o proprietário dos arquivos para o usuário atual:
```bash
$ sudo make docker-fix
```
## Remover os containers + banco de dados
Para apagar os containers e o banco de dados, execute o seguinte comando:
```bash
$ sudo make docker-remove-all
```
*Observação*: Esse comando **não** irá deletar qualquer arquivo do projeto / diretório local, apenas os containers.
## Versão de produção
Para fazer o deploy do adega na versão de produção primeiro verifique se settings.py está com a seguinte linha:
```python
DEBUG = False
```
Então execute:
```bash
$ sudo make docker-production
```
Este comando funciona da mesma forma que `make docker-up` e portando também funciona com os comandos `make docker-manage`.
O aplicativo vai rodar na porta 8000, para alterar mude a porta do container nginx no arquivo `docker-production.yml`.
### Observações do servidor
Se não for possível construir as imgens do docker no servidor será necessário copia-las manualmente por scp.
Para salvar uma imagem execute:
```bash
$ sudo docker save imagem -o imagem_destino
```
Para carregar:
```bash
$ sudo docker load -i imagem
```
É necessário carregar as imagens `adega_web`, `adega_db` e `adega_nginx`.
Se alterações forem feitas no código do adega elas serão automaticamente refletidas no servidor, mesmo na versão de produção.
Porém se mudanças forem feitas no container do nginx a imagem deste deverá ser refeita.
Para manter o aplicativo atualizado só é necessário dar pull na branch production.
## Instalação e dependências manuais (não recomendado)
```bash
......@@ -39,8 +127,8 @@ sudo -u postgres psql < postgres/create.sql
```
se você possui o arquivo do banco de dados compartilhado internamente pelos
desenvolvedores do projeto coloque-o na home do projeto, ele vem com um usuário
se você possui o arquivo do banco de dados compartilhado internamente pelos
desenvolvedores do projeto coloque-o na home do projeto, ele vem com um usuário
`pet` com senha `pet` pré-configurado para testes.
......@@ -52,7 +140,7 @@ python manage.py createsuperuser
```
## Executando o projeto
## Executando o projeto (se você fez as instalações de forma manual)
Por padrão ele irá rodar no 127.0.0.1:8000, ative o virtualenv antes
```bash
......@@ -73,7 +161,29 @@ Ao sair do projeto execute `exit` para sair do virtualenv e evitar polui-lo
## Transformando o seu usuário em um professor
Após você logar no sistema com o seu super usuário você terá acesso ao `URL_DO_SITE/admin`, graças ao [Django admin](https://docs.djangoproject.com/en/1.10/ref/contrib/admin/) nesta tela você é capaz de gerenciar os dados salvos nas models do projeto.
Para transformar o seu usuário em professor basta clicar em `professor`e então selecionar o seu usuário e o curso. Agora se você voltar para a página inicial do sistema você deve ver uma listagem dos seus cursos.
Para transformar o seu usuário em professor basta clicar em `professor`e então selecionar o seu usuário e o curso. Agora se você voltar para a página inicial do sistema você deve ver uma listagem dos seus cursos.
## Executando análises (se vocẽ está usando docker)
Para executar as análises, acesse `localhost:8000/admin` e adicione um submission.
Após isso execute o comando:
```bash
sudo make docker-manage analyze 1 # usando o docker
```
ou
```bash
python3 manage.py analyze 1
```
Onde 1 é o id do submission.
## Gerar diagrama do projeto
......
......@@ -6,7 +6,7 @@
if ! sudo -u postgres psql adega
then
sudo -u postgres psql < postgres/create.sql
sudo -u postgres psql < postgres/create.sql
fi
......
FROM python:3.5
RUN apt-get update -qq
RUN apt-get install -y \
python3-pip libpq-dev \
postgresql-client
RUN mkdir /adega
WORKDIR /adega
# Not necessary (only do the build slow)
# ADD . /adega
ADD ./requirements.txt /adega/requirements.txt
RUN pip3 install -r requirements.txt
# Not necessary. The migrate can be done by makefile or in docker-compose.yml
# Besides that, there is no database to migrate yet (while docker build)
# RUN ./install.sh --configure
version: '3'
services:
db:
container_name: adega_db_1
image: postgres
environment:
- POSTGRES_USER=adega
- POSTGRES_PASSWORD=adega
- POSTGRES_DB=adega
web:
container_name: adega_web_1
build:
context: .
dockerfile: ./docker_scripts/Dockerfile
command: bash "./docker_scripts/on_docker_init.sh"
volumes:
- .:/adega
ports:
- "8000:8000"
links:
- db
depends_on:
- db
environment:
- POSTGRES_USER=adega
- POSTGRES_PASSWORD=adega
- POSTGRES_DB=adega
- POSTGRES_HOST=adega_db_1
- VERSION=DEVELOPMENT
version: '3'
services:
db:
image: postgres
environment:
- POSTGRES_USER=adega
- POSTGRES_PASSWORD=adega
- POSTGRES_DB=adega
web:
build:
context: .
dockerfile: ./docker_scripts/Dockerfile
command: bash "./docker_scripts/on_docker_init_production.sh"
volumes:
- .:/adega
# ports:
# - "8000:8000"
links:
- db
depends_on:
- db
environment:
- POSTGRES_USER=adega
- POSTGRES_PASSWORD=adega
- POSTGRES_DB=adega
- POSTGRES_HOST=adega_db_1
- VERSION=PRODUCTION
nginx:
restart: always
build: ./nginx/
ports:
- "8000:80"
depends_on:
- web
links:
- web:web
volumes:
- ./static:/adega/static
# This commands will be run inside of the container web
# 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 uploads course
python manage.py migrate
python manage.py runserver 0.0.0.0:8000
# This commands will be run inside of the container web
# 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 uploads course
python manage.py migrate
python manage.py collectstatic --noinput
#chmod 775 -R adega/static
#python manage.py runserver 0.0.0.0:8000
gunicorn adega.wsgi:application --workers 2 --timeout 600 -b :8000
#!/bin/bash
# wait-for-postgres.sh
set -e
host="$1"
cmd="$@"
until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$POSTGRES_HOST" -U "$POSTGRES_USER" -c '\q'; do
>&2 echo "Postgres is unavailable - sleeping"
sleep 1
done
>&2 echo "Postgres is up"
#~ >&2 echo "Postgres is up - executing command"
#~ exec $cmd
......@@ -6,8 +6,7 @@ After=network.target
User=www-data
Group=www-data
WorkingDirectory=/var/www/adega/src
ExecStart=/var/www/adega/venv/bin/gunicorn --access-logfile - -k eventlet --workers 4 --bind unix:/var/www/adega/adega.sock adega.wsgi:application
ExecStart=/var/www/adega/venv/bin/gunicorn --access-logfile - -k eventlet --workers 2 --timeout 300 --bind unix:/var/www/adega/adega.sock adega.wsgi:application
[Install]
WantedBy=multi-user.target
......@@ -36,3 +36,38 @@ install-user:
pipenv install
# TODO: Create the files in docker with $USER owner
docker-fix:
chown -R $USER:$USER *
docker-up:
docker-compose --project-directory . -f docker_scripts/docker-compose.yml -p adega up
docker-production:
docker-compose --project-directory . -f docker_scripts/docker-production.yml -p adega up
docker-remove-all:
docker rm adega_web_1 adega_db_1
docker rmi adega_web
# Maybe this will not works in all OS systems
docker-install:
apt-get install docker
apt-get install docker-compose
# The follows commands permit to use manage.py with make. Examples:
# make docker-manage migrate
# make docker-manage makemigrations uploads
%:
@:
args = `arg="$(filter-out $@,$(MAKECMDGOALS))" && echo $${arg:-${1}}`
docker-manage:
@echo $(call args,"")
docker exec -it adega_web_1 bash -c "cd src; python3 manage.py $(call args,'')"
FROM tutum/nginx
RUN rm /etc/nginx/sites-enabled/default
ADD sites-enabled/ /etc/nginx/sites-enabled
......@@ -31,7 +31,7 @@ server {
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
charset utf-8;
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
......@@ -75,7 +75,7 @@ server {
disable_symlinks off;
}
location /adega {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
......@@ -84,13 +84,13 @@ server {
proxy_pass http://adega/;
}
location /adega/static {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
root /var/www;
autoindex on;
try_files $uri $uri/ =404;
......@@ -102,5 +102,3 @@ server {
deny all;
}
}
server {
listen 80;
server_name localhost;
charset utf-8;
client_max_body_size 100M;
proxy_connect_timeout 600;
proxy_read_timeout 600;
uwsgi_read_timeout 600;
fastcgi_read_timeout 600;
keepalive_timeout 600;
location /static/ {
alias /adega/static/;
}
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
CREATE DATABASE adega;
CREATE USER adega WITH PASSWORD 'adega';
-- ~ CREATE USER adega WITH PASSWORD 'adega';
ALTER ROLE adega SET client_encoding TO 'utf8';
ALTER ROLE adega SET default_transaction_isolation TO 'read committed';
ALTER ROLE adega SET timezone TO 'UTC-3';
GRANT ALL PRIVILEGES ON DATABASE adega TO adega;
-- ~ ALTER ROLE adega SET client_encoding TO 'utf8';
-- ~ ALTER ROLE adega SET default_transaction_isolation TO 'read committed';
-- ~ ALTER ROLE adega SET timezone TO 'UTC-3';
-- ~ GRANT ALL PRIVILEGES ON DATABASE adega TO adega;
import numpy as np
from utils.situations import *
ANO_ATUAL = 2017
SEMESTRE_ATUAL = 2
def listagem_turma_ingresso(df):
#~ print(df.groupby(["ANO_INGRESSO", "SEMESTRE_INGRESSO"]).groups)
grupos = df.groupby(["ANO_INGRESSO", "SEMESTRE_INGRESSO"]).groups
for t in grupos:
print(t)
print("\n\n")
print(df["FORMA_INGRESSO"][grupos[t]].drop_duplicates())
This diff is collapsed.
import pandas as pd
import math
import ujson as json
from utils.situations import Situation, EvasionForm
def average_graduation(df):
total_student = df['MATR_ALUNO'].drop_duplicates().shape[0]
total_graduate = df[df.FORMA_EVASAO == EvasionForm.EF_FORMATURA].shape[0]
return total_graduate / total_student
def general_failure(df):
affect_ira = df[df.SITUACAO.isin(Situation.SITUATION_AFFECT_IRA)]
failures = affect_ira[affect_ira.SITUACAO.isin(Situation.SITUATION_FAIL)]
average = failures.shape[0] / affect_ira.shape[0]
student_courses = affect_ira.groupby(['MATR_ALUNO'], as_index=False)\
.aggregate({'SITUACAO': 'count'})
student_failures = failures.groupby(['MATR_ALUNO'], as_index=False)\
.aggregate({'SITUACAO': 'count'})
merged = pd.merge(student_courses, student_failures, on=['MATR_ALUNO'])
merged.columns = ['MART_ALUNO', 'FEITAS', 'REPROVADO']
variance = merged['REPROVADO'].div(merged['FEITAS']).sub(average)\
.pow(2).sum() / merged.shape[0]
standard_deviation = math.sqrt(variance)
return (average, standard_deviation)
def current_students_failure(df):
fixed = df.loc[(df.FORMA_EVASAO == EvasionForm.EF_ATIVO)]
affect_ira = fixed[fixed.SITUACAO.isin(Situation.SITUATION_AFFECT_IRA)]
failures = affect_ira[affect_ira.SITUACAO.isin(Situation.SITUATION_FAIL)]
average = failures.shape[0] / affect_ira.shape[0]
student_courses = affect_ira.groupby(['MATR_ALUNO'], as_index=False)\
.aggregate({'SITUACAO': 'count'})
student_failures = failures.groupby(['MATR_ALUNO'], as_index=False)\
.aggregate({'SITUACAO': 'count'})
merged = pd.merge(student_courses, student_failures, on=['MATR_ALUNO'])
merged.columns = ['MART_ALUNO', 'FEITAS', 'REPROVADO']
variance = merged['REPROVADO'].div(merged['FEITAS']).sub(average)\
.pow(2).sum() / merged.shape[0]
standard_deviation = math.sqrt(variance)
return (average, standard_deviation)
def general_ira(df):
fixed = df[df.SITUACAO.isin(Situation.SITUATION_AFFECT_IRA)]
fixed = fixed[fixed.MEDIA_FINAL <= 100]
return (fixed.MEDIA_FINAL.mean(), fixed.MEDIA_FINAL.std())
def current_ira(df):
ano_grade = int(df.loc[df['NUM_VERSAO'].idxmax()]['NUM_VERSAO'])
fixed = df.loc[(df['NUM_VERSAO'] == ano_grade)]
fixed = fixed[fixed.SITUACAO.isin(Situation.SITUATION_AFFECT_IRA)]
fixed = fixed[fixed.MEDIA_FINAL <= 100]
return (fixed.MEDIA_FINAL.mean(), fixed.MEDIA_FINAL.std())
def current_students_ira(df):
fixed = df.loc[(df.FORMA_EVASAO == EvasionForm.EF_ATIVO)]
fixed = fixed[fixed.SITUACAO.isin(Situation.SITUATION_AFFECT_IRA)]
fixed = fixed[fixed.MEDIA_FINAL <= 100]
return (fixed.MEDIA_FINAL.mean(), fixed.MEDIA_FINAL.std())
def general_evasion_rate(df):
students = df['MATR_ALUNO'].drop_duplicates()
total_student = students.shape[0]
total_evasion = students.loc[(df.FORMA_EVASAO != EvasionForm.EF_ATIVO) & (df.FORMA_EVASAO != EvasionForm.EF_FORMATURA) & (df.FORMA_EVASAO != EvasionForm.EF_REINTEGRACAO)].shape[0]
return total_evasion / total_student
def current_evasion_rate(df):
ano_grade = int(df.loc[df['NUM_VERSAO'].idxmax()]['NUM_VERSAO'])
students = df.loc[(df['NUM_VERSAO'] == ano_grade)]
students = students['MATR_ALUNO'].drop_duplicates()
total_student = students.shape[0]
total_evasion = students.loc[(df.FORMA_EVASAO != EvasionForm.EF_ATIVO) & (df.FORMA_EVASAO != EvasionForm.EF_FORMATURA) & (df.FORMA_EVASAO != EvasionForm.EF_REINTEGRACAO)].shape[0]
return total_evasion / total_student
def average_graduation_time(df):
graduates = df.loc[(df.FORMA_EVASAO == EvasionForm.EF_FORMATURA)]
total_graduate = graduates.shape[0]
average_time = 0
year_end = int(df['ANO'].max())
semester_end = graduates['PERIODO'].max()
for index, row in graduates.iterrows():
if pd.notnull(row['ANO_EVASAO']):
year_end = int(row['ANO_EVASAO'])
try:
semester_end = int(row['SEMESTRE_EVASAO'])
except ValueError:
semester_end = graduates['PERIODO'].max()
year = int(row['ANO_INGRESSO'])
semester = int(row['SEMESTRE_INGRESSO'])
difference = 2 * (year_end - year) + (semester_end - semester) + 1
average_time += difference
average_time /= total_graduate
average_time /= 2
return average_time
def total_students(df):
return df.drop_duplicates('MATR_ALUNO').shape[0]
def current_total_students(df):
return df.loc[(df.FORMA_EVASAO == EvasionForm.EF_ATIVO)].drop_duplicates('MATR_ALUNO').shape[0]
def taxa_abandono(df):
students = df['MATR_ALUNO'].drop_duplicates()
total_student = students.shape[0]
total_abandono = students.loc[(df.FORMA_EVASAO == EvasionForm.EF_ABANDONO)].shape[0]
return total_abandono / total_student
def average_ira_graph(df):
alunos = df.drop_duplicates('MATR_ALUNO')
dic = build_dict_ira_medio(alunos)
return dic
def current_students_average_ira_graph(df):
alunos_se = df.loc[(df.FORMA_EVASAO == EvasionForm.EF_ATIVO)]
alunos_se = alunos_se.drop_duplicates('MATR_ALUNO')
dic_se = build_dict_ira_medio(alunos_se)
return dic_se
def graduates_average_ira_graph(df):
alunos_for = df.loc[(df.FORMA_EVASAO == EvasionForm.EF_FORMATURA)]
alunos_for = alunos_for.drop_duplicates('MATR_ALUNO')
dic_for = build_dict_ira_medio(alunos_for)
return dic_for
def period_evasion_graph(df):
di_qtd = {}
dic = {}
evasions_total = 0
year_start = int(df['ANO'].min())
year_end = int(df['ANO'].max()) + 1
students = df.drop_duplicates()
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']