From 7c115fd274c7c8b59f9382c74645b29ce496766a Mon Sep 17 00:00:00 2001 From: Taciano Morais Silva Date: Tue, 15 Sep 2026 15:59:09 -0300 Subject: [PATCH 1/6] chore: consolidate environment configuration Replace legacy environment samples with a sanitized root .env.example, update CI and documentation, and record the test database setup for coding agents. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .env => .env.example | 17 +++----- .github/workflows/pythonapp.yml | 4 +- .gitignore | 1 - AGENTS.md | 77 +++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 1 - README.md | 24 ++++------ contrib/env-sample | 26 ----------- contrib/path.env-sample | 2 - 8 files changed, 95 insertions(+), 57 deletions(-) rename .env => .env.example (73%) create mode 100644 AGENTS.md delete mode 100644 contrib/env-sample delete mode 100644 contrib/path.env-sample diff --git a/.env b/.env.example similarity index 73% rename from .env rename to .env.example index b91e6ba5..3387ae07 100644 --- a/.env +++ b/.env.example @@ -1,7 +1,7 @@ DJANGO_SETTINGS_MODULE=suggestclasses.settings ALLOWED_HOSTS='localhost, 127.0.0.1, labens.dct.ufrn.br' -SECRET_KEY='guvd43hkr*w1q%0og-cqwuk(j)8yii!ke5jlaw(lfn7-u@)!4g' +SECRET_KEY='your-secret-key-here' DEBUG=True CSRF_COOKIE_SECURE=True @@ -21,18 +21,15 @@ SECURE_SSL_REDIRECT=False SECURE_HSTS_SECONDS=60 SECURE_HSTS_PRELOAD=True -DATABASE_NAME=scdb_prod -DATABASE_USER=sc_user -DATABASE_PASSWORD=dct-sc-2022 +DATABASE_NAME=scdb_dev +DATABASE_USER=your_database_user +DATABASE_PASSWORD=your_database_password -DATABASE_TEST=scdb_test - -DATABASE_HOST=postgres16-server -#DATABASE_HOST=localhost +DATABASE_HOST=localhost DATABASE_PORT=5432 -EMAIL_USER=labensufrn@gmail.com -EMAIL_PASS=qqpnyixpysxluivj +EMAIL_USER=your_email@example.com +EMAIL_PASS=your_email_password PYTHONPATH=${PYTHONPATH}:/code:. diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 3d63a93c..e55b897c 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -56,7 +56,9 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Copying configurations run: | - cp contrib/env-sample .env + cp .env.example .env + sed -i "s/^DATABASE_USER=.*/DATABASE_USER=postgres/" .env + sed -i "s/^DATABASE_PASSWORD=.*/DATABASE_PASSWORD=postgres/" .env source .env - name: Test with manage.py test run: | diff --git a/.gitignore b/.gitignore index 31883a3d..1602f3f0 100644 --- a/.gitignore +++ b/.gitignore @@ -114,7 +114,6 @@ venv.bak/ dados/*.csv -path.env /static/admin/ /static/css/ /static/js/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..1b73a9fc --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,77 @@ +# Guide for Coding Agents + +## Local environment + +- Use the project virtual environment: + + ```bash + source .venv/bin/activate + ``` + +- Create local configuration from the tracked template and load it before + running Django commands or data scripts: + + ```bash + cp .env.example .env + # Edit the values required by the local environment. + source .env + ``` + +- Do not commit `.env`. It contains local credentials. Keep secrets only in + local environment files or in CI secrets. +- `DJANGO_SETTINGS_MODULE=suggestclasses.settings` and `PYTHONPATH` are set by + `.env`; there is no separate `path.env`. + +## Database and tests + +- The application reads the default database configuration from + `DATABASE_NAME`, `DATABASE_USER`, `DATABASE_PASSWORD`, `DATABASE_HOST`, and + `DATABASE_PORT`. +- No custom Django test database name is configured. Django uses + `test_`; for example, `DATABASE_NAME=scdb_dev` uses + `test_scdb_dev`. +- To let Django create and remove test databases, grant the database role + `CREATEDB`: + + ```sql + ALTER ROLE sc_user WITH CREATEDB; + ``` + +- On PostgreSQL 15+, ensure the role can create objects in the `public` schema + of the test database. In this environment, the role also needed `INHERIT`: + + ```sql + ALTER ROLE sc_user INHERIT; + ``` + +- Run tests with: + + ```bash + python manage.py test --keepdb + ``` + + `--keepdb` preserves the test database between executions. + +## CI + +- The GitHub Actions workflow copies `.env.example` to `.env` and then replaces + the database credentials with those of its PostgreSQL service + (`postgres`/`postgres`). +- Keep `.env.example` free of real credentials and compatible with the + variables read by `suggestclasses/settings.py`. + +## Session changes recorded + +- Removed the unused `DATABASE_TEST` variable. It was not read by the Django + settings, which rely on Django's default test database naming. +- Added `.env.example` at the repository root with placeholders for sensitive + values. +- Updated the README and CI workflow to use `.env.example`. +- Removed the obsolete `contrib/` directory and all `path.env` documentation. +- Removed the obsolete `contrib/` entry from `CONTRIBUTING.md` and the obsolete + `path.env` rule from `.gitignore`. +- The test database permission issue was reproduced and resolved locally after + granting `CREATEDB` and `INHERIT` to the database role. +- The test suite still has one unrelated pre-existing failure: + `core/tests/test_historico.py` uses removed `assertEquals`; use + `assertEqual` when addressing that test separately. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4fa80e8d..aa8245c4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,6 @@ Nosso documentação se concentra na pasta `docs/` deste repositório, aqui uma | Arquivo/Pasta | Descrição | |--- |--- | -| `contrib/` | Contém arquivos de configuração do ambiente de desenvolvimento.| | `core/` | Aplicativo Django, código base de todo o sistema. | | `dados/` | Contém scripts para downloads dos dados e povoamento da base de dados. | | `docs/` | Contém arquivos de documentação e imagens da modelagem do Sistema. | diff --git a/README.md b/README.md index 7e72f89b..6c06bdcd 100644 --- a/README.md +++ b/README.md @@ -59,26 +59,18 @@ Criação do Ambiente Virtual com [python3-venv](https://docs.python.org/pt-br/3 python3 -m venv .venv ``` -Para ativar: ```source venv/bin/activate```. +Para ativar: ```source .venv/bin/activate```. Para desativar: ```deactivate```. -Copie os exemplos destes arquivos no diretório raiz do projeto: +Copie o arquivo de exemplo para o diretório raiz do projeto: ```console -cp .env.sample .env -cp path.env.sample path.env +cp .env.example .env ``` -Edite o arquivo `path.env` para informar as variáveis: -```console -export DJANGO_SETTINGS_MODULE=suggestclasses.settings -export PYTHONPATH=${PYTHONPATH}:/home//suggestclasses -``` - -Após editar os valores, execute os comandos: +Edite o arquivo `.env` para informar as variáveis e carregue-o: ```console source .env -source path.env ``` ### Execução do Projeto @@ -118,7 +110,7 @@ Lembre-se: O SGBD deve estar em execução e configure o acesso no arquivo `.env O sistema é baseado nos dados abertos da UFRN, desta forma é necessário povoar o banco de dados com informações de Horários, Centro, Salas, Departamentos, Componentes, etc. -Lembre-se de deixar todas as variáveis de ambiente definidas. Execute o comando `source path.env` no Linux ou `activate path.env` no Windows para carregar as variáveis. +Lembre-se de configurar e carregar todas as variáveis de ambiente com `source .env`. A ordem é importante e deve ser seguida conforme descrito abaixo. @@ -164,7 +156,7 @@ django.setup() Configurações executar os testes: ```shell script -export DJANGO_SETTINGS_MODULE=suggestclasses.settings +source .env python manage.py test ``` @@ -172,7 +164,7 @@ python manage.py test #### Utilizando o Cobetura -Primeiro defina a varíavel de ambiente: ```export DJANGO_SETTINGS_MODULE=projectname.settings```. +Primeiro carregue as variáveis de ambiente: `source .env`. Depois instale o **coverage** e rode para ele gerar o arquivo `coverage.xml`. @@ -189,7 +181,7 @@ Se usar `coverage html`, ele gera o relatório em html. ```shell script pip install nose pip install coverage -export DJANGO_SETTINGS_MODULE=suggestclasses.settings +source .env ``` ```shell script diff --git a/contrib/env-sample b/contrib/env-sample deleted file mode 100644 index dbe0d202..00000000 --- a/contrib/env-sample +++ /dev/null @@ -1,26 +0,0 @@ -DJANGO_SETTINGS_MODULE=suggestclasses.settings -ALLOWED_HOSTS='localhost, 127.0.0.1, testserver' - -SECRET_KEY='cg#p$g+j9tax!#a3cup@1$8obt2_+&k3q+pmu)5%asj6yjpkag' -DEBUG=True - -DATABASE_NAME=scdb_dev -DATABASE_USER=postgres -DATABASE_PASSWORD=postgres - -DATABASE_HOST=localhost -DATABASE_PORT=5432 - -EMAIL_USER= -EMAIL_PASS= - -# CORS -CORS_ALLOW_ALL_ORIGINS=True -CORS_ALLOWED_ORIGINS=http://labens.dct.ufrn.br,https://labens.dct.ufrn.br,http://localhost:8000 -CSRF_TRUSTED_ORIGINS=http://labens.dct.ufrn.br,https://labens.dct.ufrn.br,http://localhost:8000 - -# Project ROOT DIR -PYTHONPATH=${PYTHONPATH}:. - -# Docker WORKDIR -PYTHONPATH=${PYTHONPATH}:/code diff --git a/contrib/path.env-sample b/contrib/path.env-sample deleted file mode 100644 index 22d2149f..00000000 --- a/contrib/path.env-sample +++ /dev/null @@ -1,2 +0,0 @@ -export DJANGO_SETTINGS_MODULE=suggestclasses.settings -export PYTHONPATH=${PYTHONPATH}:/home//suggestclasses From 796d7d96403de54732c4bf84db940f5f17ae1feb Mon Sep 17 00:00:00 2001 From: Taciano Morais Silva Date: Tue, 15 Sep 2026 16:08:39 -0300 Subject: [PATCH 2/6] chore: set example database credentials Use the development database role and password in the environment template. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .env.example | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index 3387ae07..f4ae7261 100644 --- a/.env.example +++ b/.env.example @@ -22,8 +22,8 @@ SECURE_HSTS_SECONDS=60 SECURE_HSTS_PRELOAD=True DATABASE_NAME=scdb_dev -DATABASE_USER=your_database_user -DATABASE_PASSWORD=your_database_password +DATABASE_USER=sc_user +DATABASE_PASSWORD=sc_pass DATABASE_HOST=localhost DATABASE_PORT=5432 From 76663fcba2362dc04d3aa13526d8325c434081b7 Mon Sep 17 00:00:00 2001 From: Taciano Morais Silva Date: Tue, 15 Sep 2026 16:17:32 -0300 Subject: [PATCH 3/6] docs: update test execution commands Document the Django test runner for regular and coverage runs, removing obsolete Nose and unittest discovery instructions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 6c06bdcd..cab22397 100644 --- a/README.md +++ b/README.md @@ -157,36 +157,14 @@ Configurações executar os testes: ```shell script source .env -python manage.py test +python manage.py test --keepdb ``` ### Executar os Testes de Unidade e Cobertura -#### Utilizando o Cobetura - -Primeiro carregue as variáveis de ambiente: `source .env`. - -Depois instale o **coverage** e rode para ele gerar o arquivo `coverage.xml`. - -```pythonstub -pip install coverage -coverage run -m unittest discover -coverage xml -``` - -Se usar `coverage html`, ele gera o relatório em html. - -#### Utilizando Nose (desativado) - ```shell script -pip install nose -pip install coverage source .env -``` - -```shell script -nosetests --with-xunit -nosetests --with-coverage --cover-package=core --cover-branches --cover-xml +coverage run manage.py test ``` ## Executar o Sonar From 068137152d2ef3e8462221bd8f5ca9089c357f66 Mon Sep 17 00:00:00 2001 From: Taciano Morais Silva Date: Tue, 15 Sep 2026 16:26:14 -0300 Subject: [PATCH 4/6] fix: make environment example sourceable Quote values containing spaces so the CI setup can source the generated environment file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .env.example | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index f4ae7261..08057cae 100644 --- a/.env.example +++ b/.env.example @@ -8,10 +8,10 @@ CSRF_COOKIE_SECURE=True # CORS CORS_ALLOW_ALL_ORIGINS=True -CORS_ALLOWED_ORIGINS=http://labens.dct.ufrn.br, https://labens.dct.ufrn.br, http://localhost:8003 -CSRF_TRUSTED_ORIGINS=http://labens.dct.ufrn.br, https://labens.dct.ufrn.br, http://localhost:8003 +CORS_ALLOWED_ORIGINS='http://labens.dct.ufrn.br, https://labens.dct.ufrn.br, http://localhost:8003' +CSRF_TRUSTED_ORIGINS='http://labens.dct.ufrn.br, https://labens.dct.ufrn.br, http://localhost:8003' -SESSION_COOKIE_AGE=60 * 30 # 1 hora +SESSION_COOKIE_AGE='60 * 30' # 1 hora SESSION_COOKIE_SECURE=True SESSION_EXPIRE_AT_BROWSER_CLOSE=True SECURE_CONTENT_TYPE_NOSNIFF=True From 30237c7bdded50e7b1e1a1c65d441fb22bad6082 Mon Sep 17 00:00:00 2001 From: Taciano Morais Silva Date: Tue, 15 Sep 2026 16:47:33 -0300 Subject: [PATCH 5/6] fix: load session settings from environment Use sourceable origin lists and configure the session duration from the environment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .env.example | 7 +++---- suggestclasses/settings.py | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.env.example b/.env.example index 08057cae..4cf41079 100644 --- a/.env.example +++ b/.env.example @@ -8,10 +8,9 @@ CSRF_COOKIE_SECURE=True # CORS CORS_ALLOW_ALL_ORIGINS=True -CORS_ALLOWED_ORIGINS='http://labens.dct.ufrn.br, https://labens.dct.ufrn.br, http://localhost:8003' -CSRF_TRUSTED_ORIGINS='http://labens.dct.ufrn.br, https://labens.dct.ufrn.br, http://localhost:8003' - -SESSION_COOKIE_AGE='60 * 30' # 1 hora +CORS_ALLOWED_ORIGINS="http://labens.dct.ufrn.br,https://labens.dct.ufrn.br,http://localhost:8003" +CSRF_TRUSTED_ORIGINS="http://labens.dct.ufrn.br,https://labens.dct.ufrn.br,http://localhost:8003" +SESSION_COOKIE_AGE=1800 SESSION_COOKIE_SECURE=True SESSION_EXPIRE_AT_BROWSER_CLOSE=True SECURE_CONTENT_TYPE_NOSNIFF=True diff --git a/suggestclasses/settings.py b/suggestclasses/settings.py index 0350368c..207ac270 100644 --- a/suggestclasses/settings.py +++ b/suggestclasses/settings.py @@ -41,7 +41,7 @@ CORS_ALLOWED_ORIGINS = config('CORS_ALLOWED_ORIGINS', cast=Csv()) CSRF_TRUSTED_ORIGINS = config('CSRF_TRUSTED_ORIGINS', cast=Csv()) -SESSION_COOKIE_AGE=60 * 30 # meia hora +SESSION_COOKIE_AGE = config('SESSION_COOKIE_AGE', default=60 * 30, cast=int) SESSION_COOKIE_SECURE=config('SESSION_COOKIE_SECURE', default=False, cast=bool) SESSION_EXPIRE_AT_BROWSER_CLOSE = config('SESSION_EXPIRE_AT_BROWSER_CLOSE', default=True, cast=bool) From 8422adafd123b00a1850988512fb7557dedd9d97 Mon Sep 17 00:00:00 2001 From: Taciano Morais Silva Date: Tue, 15 Sep 2026 16:55:03 -0300 Subject: [PATCH 6/6] chore: remove obsolete plotting code Remove the unused commented Matplotlib plotting implementation and its imports from the views module. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- core/views.py | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/core/views.py b/core/views.py index 0006aa29..66d129cd 100644 --- a/core/views.py +++ b/core/views.py @@ -1,11 +1,8 @@ from core.bo.historico import listar_historicos_by_discente -import io import logging -from random import sample from typing import List from django.db.models.expressions import OuterRef, Subquery -# import matplotlib.pyplot as plt from django.contrib import messages from django.contrib.auth import authenticate, logout, login, update_session_auth_hash from django.contrib.auth.decorators import login_required, permission_required @@ -18,7 +15,6 @@ from django.template import loader from django.views.decorators.http import require_http_methods from django.views.generic import DetailView -# from matplotlib.backends.backend_agg import FigureCanvasAgg from core.config.config import get_config from core.models import Curso, ComponenteCurricular, EstruturaCurricular, SugestaoTurma, Sala, Docente, Turma, \ @@ -980,38 +976,3 @@ def enquete_votos_listar(request, pk, cc_pk): } return render(request, 'core/enquetes/votos_listar.html', context) - - - - -# def plot(request): -# # Creamos los datos para representar en el gráfico -# x = range(1, 11) -# y = sample(range(20), len(x)) -# -# # Creamos una figura y le dibujamos el gráfico -# f = plt.figure() -# -# # Creamos los ejes -# axes = f.add_axes([0.15, 0.15, 0.75, 0.75]) # [left, bottom, width, height] -# axes.plot(x, y) -# axes.set_xlabel("Eje X") -# axes.set_ylabel("Eje Y") -# axes.set_title("Mi gráfico dinámico") - -# # Como enviaremos la imagen en bytes la guardaremos en un buffer -# buf = io.BytesIO() -# canvas = FigureCanvasAgg(f) -# canvas.print_png(buf) - -# # Creamos la respuesta enviando los bytes en tipo imagen png -# response = HttpResponse(buf.getvalue(), content_type='image/png') - -# # Limpiamos la figura para liberar memoria -# f.clear() - -# # Añadimos la cabecera de longitud de fichero para más estabilidad -# response['Content-Length'] = str(len(response.content)) - -# # Devolvemos la response -# return response