From 196edd4baa5f5979664c1cc126f09f2f12750c7e Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 5 Nov 2023 16:46:25 -0300 Subject: [PATCH 01/33] Co-authored-by: Gabriel Henrique Castelo --- api/api/tests.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/api/api/tests.py b/api/api/tests.py index 7ce503c2..eb995cc4 100644 --- a/api/api/tests.py +++ b/api/api/tests.py @@ -1,3 +1,37 @@ from django.test import TestCase +from .models import Department, Discipline -# Create your tests here. +class DisciplineTests(TestCase): + def setUp(self): + self.department = Department.objects.create( + name = 'Departamento de Informática', + code = 'INF', + ) + + self.discipline = Discipline.objects.create( + name = 'Métodos de Desenvolvimento de Software', + code = 'MDS1010', + workload = 60, + teachers = ['Professor 1', 'Professor 2'], + classroom = 'MOCAP', + schedule = '46M34', + days = ['Quarta-Feira 10:00 às 11:50', 'Sexta-Feira 10:00 às 11:50'], + _class = 1, + department = self.department + ) + + + def test_create_discipline(self): + self.assertEqual(self.discipline.name, 'Métodos de Desenvolvimento de Software') + self.assertEqual(self.discipline.code, 'MDS1010') + self.assertEqual(self.discipline.workload, 60) + self.assertEqual(self.discipline.teachers, ['Professor 1', 'Professor 2']) + self.assertEqual(self.discipline.classroom, 'MOCAP') + self.assertEqual(self.discipline.schedule, '46M34') + self.assertEqual(self.discipline.days, ['Quarta-Feira 10:00 às 11:50', 'Sexta-Feira 10:00 às 11:50']) + self.assertEqual(self.discipline._class, 1) + self.assertEqual(self.discipline.department, self.department) + + def test_create_department(self): + self.assertEqual(self.department.name, 'Departamento de Informática') + self.assertEqual(self.department.code, 'INF') From 3c54d8ffaf8564e13e46ff70a0d66adb8e4bd37c Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 5 Nov 2023 17:05:09 -0300 Subject: [PATCH 02/33] db(migrations): update migrations --- api/api/migrations/0001_initial.py | 39 ++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 api/api/migrations/0001_initial.py diff --git a/api/api/migrations/0001_initial.py b/api/api/migrations/0001_initial.py new file mode 100644 index 00000000..c48c23dc --- /dev/null +++ b/api/api/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 4.2.5 on 2023-11-05 19:30 + +import django.contrib.postgres.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Department', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50, unique=True)), + ('code', models.CharField(max_length=10, unique=True)), + ], + ), + migrations.CreateModel( + name='Discipline', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=50)), + ('code', models.CharField(max_length=10, unique=True)), + ('workload', models.IntegerField()), + ('teachers', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), size=None)), + ('classroom', models.CharField(max_length=50)), + ('schedule', models.CharField(max_length=50)), + ('days', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), size=None)), + ('_class', models.IntegerField()), + ('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='disciplines', to='api.department')), + ], + ), + ] From 13cb241947da77c012255be2759e0467ac127451 Mon Sep 17 00:00:00 2001 From: Caio Date: Sun, 5 Nov 2023 17:05:35 -0300 Subject: [PATCH 03/33] db(models): create model discipline and department --- api/api/models.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/api/api/models.py b/api/api/models.py index 71a83623..89ecda94 100644 --- a/api/api/models.py +++ b/api/api/models.py @@ -1,3 +1,25 @@ from django.db import models +from django.contrib.postgres.fields import ArrayField -# Create your models here. +class Department(models.Model): + # Classe que representa um departamento + name = models.CharField(max_length=50, unique=True) + code = models.CharField(max_length=10, unique=True) + + def __str__(self): + return self.name + +class Discipline(models.Model): + # Classe que representa uma disciplina + name = models.CharField(max_length=50) + code = models.CharField(max_length=10, unique=True) + workload = models.IntegerField() + teachers = ArrayField(models.CharField(max_length=50)) + classroom = models.CharField(max_length=50) + schedule = models.CharField(max_length=50) + days = ArrayField(models.CharField(max_length=50)) + _class = models.IntegerField() + department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='disciplines') + + def __str__(self): + return self.name \ No newline at end of file From 3eff6b728a9b35bb68eef6d52a3e67a2e274e73e Mon Sep 17 00:00:00 2001 From: GabrielCastelo-31 Date: Sun, 5 Nov 2023 17:52:36 -0300 Subject: [PATCH 04/33] =?UTF-8?q?Coment=C3=A1rios=20adicionados=20e=20modi?= =?UTF-8?q?ficados.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/api/utils/sessions.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/api/api/utils/sessions.py b/api/api/utils/sessions.py index a59a11ae..9980c420 100644 --- a/api/api/utils/sessions.py +++ b/api/api/utils/sessions.py @@ -4,6 +4,10 @@ from datetime import datetime from typing import List +"""Este módulo contém funções necessárias para realizar uma requsição ao SIGAA +corretamente. +""" + URL = "https://sigaa.unb.br/sigaa/public/turmas/listar.jsf" HEADERS = { 'Access-Control-Allow-Origin': '*', @@ -13,7 +17,7 @@ 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0' } -'''Create a request session with retry and backoff_factor''' +"""Cria uma sessão de requisição e retorna um objeto Session.""" def create_request_session() -> Session: session = Session() # Create a persistent request session retry = Retry(connect=3, backoff_factor=0.5) # Create a retry object @@ -22,14 +26,15 @@ def create_request_session() -> Session: return session -'''Get the response from the request session''' +"""Obtem a resposta da requisição ao SIGAA e retorna um objeto Response.""" def get_response(session: Session) -> Response: response = session.get(url=URL, headers=HEADERS) # Make a get request to the url return response +"""Obtem o cookie da sessão de requisição necessário para acessar a pagina de turmas +e retorna um cookie jar.""" def get_session_cookie(session: Session) -> cookies.RequestsCookieJar: - '''Get the cookie from the request session''' response = get_response(session) # Get the response from the request session cookie = response.cookies.get_dict() # Get the cookie from the response cookie_jar = cookies.RequestsCookieJar() # Create a cookie jar @@ -37,9 +42,9 @@ def get_session_cookie(session: Session) -> cookies.RequestsCookieJar: return cookie_jar +"""Obtem o ano e o período atual e retorna uma lista com esses valores.""" def get_current_year_and_period() -> List[int | str]: - # Pega o ano e o período atual - current_date = datetime.now() + current_date = datetime.now() current_year = current_date.year period = "1" @@ -47,4 +52,4 @@ def get_current_year_and_period() -> List[int | str]: if datetime(current_year, 5, 1) <= current_date <= datetime(current_year, 10, 30): period = "2" - return [current_year, period] \ No newline at end of file + return [current_year, period] From 0a75551597168c3701e2cb7889a9aa74eb00fd95 Mon Sep 17 00:00:00 2001 From: GabrielCastelo-31 Date: Sun, 5 Nov 2023 17:54:45 -0300 Subject: [PATCH 05/33] File ignored. --- .gitignore | 185 +++++++++++++++++++++++++++-------------------------- 1 file changed, 93 insertions(+), 92 deletions(-) diff --git a/.gitignore b/.gitignore index 6f9eb6d8..abba5ac0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,10 @@ __pycache__ db.sqlite3 media -# Backup files # -*.bak +# Backup files # +*.bak -# If you are using PyCharm # +# If you are using PyCharm # # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml @@ -45,93 +45,94 @@ out/ # JIRA plugin atlassian-ide-plugin.xml -# Python # -*.py[cod] -*$py.class - -# Distribution / packaging -.Python build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -*.egg-info/ -.installed.cfg -*.egg -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -.pytest_cache/ -nosetests.xml -coverage.xml -*.cover -.hypothesis/ - -# Jupyter Notebook -.ipynb_checkpoints - -# pyenv -.python-version - -# celery -celerybeat-schedule.* - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv +# Python # +*.py[cod] +*$py.class + +# Distribution / packaging +.Python build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +.pytest_cache/ +nosetests.xml +coverage.xml +*.cover +.hypothesis/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery +celerybeat-schedule.* + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ - -# Sublime Text # -*.tmlanguage.cache -*.tmPreferences.cache -*.stTheme.cache -*.sublime-workspace -*.sublime-project - -# sftp configuration file -sftp-config.json - -# Package control specific files Package -Control.last-run -Control.ca-list -Control.ca-bundle -Control.system-ca-bundle -GitHub.sublime-settings - -# Visual Studio Code # -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history \ No newline at end of file +venv/ +ENV/ +env.bak/ +venv.bak/ + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ + +# Sublime Text # +*.tmlanguage.cache +*.tmPreferences.cache +*.stTheme.cache +*.sublime-workspace +*.sublime-project + +# sftp configuration file +sftp-config.json + +# Package control specific files Package +Control.last-run +Control.ca-list +Control.ca-bundle +Control.system-ca-bundle +GitHub.sublime-settings + +# Visual Studio Code # +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history +.vscode/settings.json From 66dd5bdb97748bb1356aa843c2db2b99050d76a6 Mon Sep 17 00:00:00 2001 From: GabrielCastelo-31 Date: Sun, 5 Nov 2023 22:07:06 -0300 Subject: [PATCH 06/33] Models updated. Class class added. Co-authored-by: Caio Felipe --- api/api/models.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/api/api/models.py b/api/api/models.py index 89ecda94..10df9255 100644 --- a/api/api/models.py +++ b/api/api/models.py @@ -3,23 +3,30 @@ class Department(models.Model): # Classe que representa um departamento - name = models.CharField(max_length=50, unique=True) code = models.CharField(max_length=10, unique=True) + year = models.CharField(max_length=4, default='0000') + period = models.CharField(max_length=1, default='1') def __str__(self): - return self.name + return self.code class Discipline(models.Model): # Classe que representa uma disciplina name = models.CharField(max_length=50) code = models.CharField(max_length=10, unique=True) + department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='disciplines') + + def __str__(self): + return self.name + +class Class(models.Model): workload = models.IntegerField() teachers = ArrayField(models.CharField(max_length=50)) classroom = models.CharField(max_length=50) schedule = models.CharField(max_length=50) days = ArrayField(models.CharField(max_length=50)) _class = models.IntegerField() - department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='disciplines') + discipline = models.ForeignKey(Discipline, on_delete=models.CASCADE, related_name='classes') def __str__(self): - return self.name \ No newline at end of file + return self._class From e9297e4526ff05360b4f9ae20ed627d51500cacd Mon Sep 17 00:00:00 2001 From: GabrielCastelo-31 Date: Sun, 5 Nov 2023 22:07:47 -0300 Subject: [PATCH 07/33] Tests updateded. Class class added to tests.py Co-authored-by: Caio Felipe --- api/api/tests.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/api/api/tests.py b/api/api/tests.py index eb995cc4..b54e59dc 100644 --- a/api/api/tests.py +++ b/api/api/tests.py @@ -1,37 +1,45 @@ from django.test import TestCase -from .models import Department, Discipline +from .models import Department, Discipline,Class class DisciplineTests(TestCase): def setUp(self): self.department = Department.objects.create( - name = 'Departamento de Informática', code = 'INF', + year = "2023", + period = "2" ) self.discipline = Discipline.objects.create( name = 'Métodos de Desenvolvimento de Software', code = 'MDS1010', + department = self.department + ) + self._class = Class.objects.create( workload = 60, teachers = ['Professor 1', 'Professor 2'], classroom = 'MOCAP', schedule = '46M34', days = ['Quarta-Feira 10:00 às 11:50', 'Sexta-Feira 10:00 às 11:50'], _class = 1, - department = self.department + discipline = self.discipline ) - + def test_create_discipline(self): self.assertEqual(self.discipline.name, 'Métodos de Desenvolvimento de Software') self.assertEqual(self.discipline.code, 'MDS1010') - self.assertEqual(self.discipline.workload, 60) - self.assertEqual(self.discipline.teachers, ['Professor 1', 'Professor 2']) - self.assertEqual(self.discipline.classroom, 'MOCAP') - self.assertEqual(self.discipline.schedule, '46M34') - self.assertEqual(self.discipline.days, ['Quarta-Feira 10:00 às 11:50', 'Sexta-Feira 10:00 às 11:50']) - self.assertEqual(self.discipline._class, 1) self.assertEqual(self.discipline.department, self.department) - + + def test_create_class(self): + self.assertEqual(self._class.workload, 60) + self.assertEqual(self._class.teachers, ['Professor 1', 'Professor 2']) + self.assertEqual(self._class.classroom, 'MOCAP') + self.assertEqual(self._class.schedule, '46M34') + self.assertEqual(self._class.days, ['Quarta-Feira 10:00 às 11:50', 'Sexta-Feira 10:00 às 11:50']) + self.assertEqual(self._class._class, 1) + self.assertEqual(self._class.discipline, self.discipline) + def test_create_department(self): - self.assertEqual(self.department.name, 'Departamento de Informática') self.assertEqual(self.department.code, 'INF') + self.assertEqual(self.department.year, '2023') + self.assertEqual(self.department.period, '2') From edfb070fe5ae66cf2de1602529a2e0dee7f5b1e3 Mon Sep 17 00:00:00 2001 From: GabrielCastelo-31 Date: Sun, 5 Nov 2023 22:11:58 -0300 Subject: [PATCH 08/33] Updating migrations. Co-authored-by: Caio Felipe --- ..._name_remove_discipline__class_and_more.py | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 api/api/migrations/0002_remove_department_name_remove_discipline__class_and_more.py diff --git a/api/api/migrations/0002_remove_department_name_remove_discipline__class_and_more.py b/api/api/migrations/0002_remove_department_name_remove_discipline__class_and_more.py new file mode 100644 index 00000000..25c6db11 --- /dev/null +++ b/api/api/migrations/0002_remove_department_name_remove_discipline__class_and_more.py @@ -0,0 +1,66 @@ +# Generated by Django 4.2.5 on 2023-11-06 00:27 + +import django.contrib.postgres.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='department', + name='name', + ), + migrations.RemoveField( + model_name='discipline', + name='_class', + ), + migrations.RemoveField( + model_name='discipline', + name='classroom', + ), + migrations.RemoveField( + model_name='discipline', + name='days', + ), + migrations.RemoveField( + model_name='discipline', + name='schedule', + ), + migrations.RemoveField( + model_name='discipline', + name='teachers', + ), + migrations.RemoveField( + model_name='discipline', + name='workload', + ), + migrations.AddField( + model_name='department', + name='period', + field=models.CharField(default='1', max_length=1), + ), + migrations.AddField( + model_name='department', + name='year', + field=models.CharField(default='0000', max_length=4), + ), + migrations.CreateModel( + name='Class', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('workload', models.IntegerField()), + ('teachers', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), size=None)), + ('classroom', models.CharField(max_length=50)), + ('schedule', models.CharField(max_length=50)), + ('days', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), size=None)), + ('_class', models.IntegerField()), + ('discipline', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='classes', to='api.discipline')), + ], + ), + ] From 450cc7972f8e59b510af07aed4074e9465add325 Mon Sep 17 00:00:00 2001 From: GabrielCastelo-31 Date: Sun, 5 Nov 2023 22:12:32 -0300 Subject: [PATCH 09/33] Web Scraping bug fixed and logic improved. funcions added. Co-authored-by: Caio Felipe --- api/api/utils/web_scraping.py | 40 ++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/api/api/utils/web_scraping.py b/api/api/utils/web_scraping.py index ac67942d..2ed36b9d 100644 --- a/api/api/utils/web_scraping.py +++ b/api/api/utils/web_scraping.py @@ -1,4 +1,4 @@ -from sessions import URL, HEADERS, get_response, create_request_session, get_session_cookie +from .sessions import URL, HEADERS, get_response, create_request_session, get_session_cookie from bs4 import BeautifulSoup from collections import defaultdict from typing import List, Optional @@ -9,7 +9,7 @@ ''' Modo de uso: -1. É necessário ter o código do departamento escolhido. +1. É necessário ter o código do departamento escolhido. Logo, temos a função get_list_of_departments() que retorna uma lista com os códigos dos departamentos. 2. É necessário ter o ano e o período. 3. É necessário ter uma instância da classe DisciplineWebScraper. @@ -27,10 +27,10 @@ def get_list_of_departments() -> Optional[List]: response = get_response(create_request_session()) # Get the response from the request session soup = BeautifulSoup(response.content, "html.parser") # Create a BeautifulSoup object departments = soup.find("select", attrs={"id": "formTurma:inputDepto"}) # Find the tag with id "formTurma:inputDepto" @@ -43,6 +43,7 @@ def get_list_of_departments() -> Optional[List]: return department_ids def get_department_disciplines(department_id: str, current_year: str, current_period: str): + """Obtem as disciplinas de um departamento""" discipline_scraper = DisciplineWebScraper(department_id, current_year, current_period) disciplines = discipline_scraper.get_disciplines() @@ -51,7 +52,7 @@ def get_department_disciplines(department_id: str, current_year: str, current_pe class DisciplineWebScraper: # Classe que faz o web scraping das disciplinas def __init__(self, department: str, year: str, period: str, session=None, cookie=None): - self.disciplines = defaultdict(list) # A dictionary with the disciplines + self.disciplines: defaultdict[str, List[dict]] = defaultdict(list) # A dictionary with the disciplines self.department = department # The department code self.period = period # 1 for first semester and 2 for second semester self.year = year @@ -117,7 +118,7 @@ def make_web_scraping_of_disciplines(self, response): Chave: Código da disciplina (string) Valor: Lista de dicionários com as seguintes chaves: - name: Nome da disciplina (string) - - class: Turma (int) + - class: Turma (str) - teachers: Nome dos professores (Lista de strings) - workload: Carga horária (int). Se não houver, o valor é -1! - classroom: Sala (string) @@ -138,13 +139,18 @@ def make_web_scraping_of_disciplines(self, response): teachers.append(content[0].strip()) - if(len(teachers) == 0): + if len(teachers) == 0: teachers.append("A definir") - class_code = int(tables_data[0].get_text()) + class_code = tables_data[0].get_text() classroom = tables_data[7].get_text().strip() - schedule, week_days = tables_data[3].get_text().strip().split(maxsplit=1) + schedule = "A definir" + week_days = "A definir" + + if len(tables_data[3].get_text().strip().split(maxsplit=1)) == 2: + schedule, week_days = tables_data[3].get_text().strip().split(maxsplit=1) + workload = self.calc_hours(schedule) sep = week_days.rfind("\t") From 3202771dcde5b00887bf3a6e8aec19b14bbe2de6 Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 6 Nov 2023 13:57:06 -0300 Subject: [PATCH 26/33] db(migrations): fix migrations files --- api/api/migrations/0001_initial.py | 30 +++++---- ..._name_remove_discipline__class_and_more.py | 66 ------------------- ...ass_classroom_alter_class_days_and_more.py | 39 ----------- api/api/migrations/0004_alter_class__class.py | 18 ----- .../migrations/0005_alter_department_code.py | 18 ----- ...r_department_code_alter_discipline_code.py | 23 ------- ...r_department_code_alter_discipline_code.py | 23 ------- 7 files changed, 19 insertions(+), 198 deletions(-) delete mode 100644 api/api/migrations/0002_remove_department_name_remove_discipline__class_and_more.py delete mode 100644 api/api/migrations/0003_alter_class_classroom_alter_class_days_and_more.py delete mode 100644 api/api/migrations/0004_alter_class__class.py delete mode 100644 api/api/migrations/0005_alter_department_code.py delete mode 100644 api/api/migrations/0006_alter_department_code_alter_discipline_code.py delete mode 100644 api/api/migrations/0007_alter_department_code_alter_discipline_code.py diff --git a/api/api/migrations/0001_initial.py b/api/api/migrations/0001_initial.py index c48c23dc..d5e363e0 100644 --- a/api/api/migrations/0001_initial.py +++ b/api/api/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2023-11-05 19:30 +# Generated by Django 4.2.5 on 2023-11-06 16:42 import django.contrib.postgres.fields from django.db import migrations, models @@ -17,23 +17,31 @@ class Migration(migrations.Migration): name='Department', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=50, unique=True)), - ('code', models.CharField(max_length=10, unique=True)), + ('code', models.CharField(max_length=10)), + ('year', models.CharField(default='0000', max_length=4)), + ('period', models.CharField(default='1', max_length=1)), ], ), migrations.CreateModel( name='Discipline', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=50)), - ('code', models.CharField(max_length=10, unique=True)), - ('workload', models.IntegerField()), - ('teachers', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), size=None)), - ('classroom', models.CharField(max_length=50)), - ('schedule', models.CharField(max_length=50)), - ('days', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), size=None)), - ('_class', models.IntegerField()), + ('name', models.CharField(max_length=100)), + ('code', models.CharField(max_length=20)), ('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='disciplines', to='api.department')), ], ), + migrations.CreateModel( + name='Class', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('workload', models.IntegerField()), + ('teachers', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), size=None)), + ('classroom', models.CharField(max_length=60)), + ('schedule', models.CharField(max_length=60)), + ('days', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=60), size=None)), + ('_class', models.CharField(max_length=30)), + ('discipline', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='classes', to='api.discipline')), + ], + ), ] diff --git a/api/api/migrations/0002_remove_department_name_remove_discipline__class_and_more.py b/api/api/migrations/0002_remove_department_name_remove_discipline__class_and_more.py deleted file mode 100644 index 25c6db11..00000000 --- a/api/api/migrations/0002_remove_department_name_remove_discipline__class_and_more.py +++ /dev/null @@ -1,66 +0,0 @@ -# Generated by Django 4.2.5 on 2023-11-06 00:27 - -import django.contrib.postgres.fields -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='department', - name='name', - ), - migrations.RemoveField( - model_name='discipline', - name='_class', - ), - migrations.RemoveField( - model_name='discipline', - name='classroom', - ), - migrations.RemoveField( - model_name='discipline', - name='days', - ), - migrations.RemoveField( - model_name='discipline', - name='schedule', - ), - migrations.RemoveField( - model_name='discipline', - name='teachers', - ), - migrations.RemoveField( - model_name='discipline', - name='workload', - ), - migrations.AddField( - model_name='department', - name='period', - field=models.CharField(default='1', max_length=1), - ), - migrations.AddField( - model_name='department', - name='year', - field=models.CharField(default='0000', max_length=4), - ), - migrations.CreateModel( - name='Class', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('workload', models.IntegerField()), - ('teachers', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), size=None)), - ('classroom', models.CharField(max_length=50)), - ('schedule', models.CharField(max_length=50)), - ('days', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), size=None)), - ('_class', models.IntegerField()), - ('discipline', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='classes', to='api.discipline')), - ], - ), - ] diff --git a/api/api/migrations/0003_alter_class_classroom_alter_class_days_and_more.py b/api/api/migrations/0003_alter_class_classroom_alter_class_days_and_more.py deleted file mode 100644 index 356c33af..00000000 --- a/api/api/migrations/0003_alter_class_classroom_alter_class_days_and_more.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 4.2.5 on 2023-11-06 13:46 - -import django.contrib.postgres.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0002_remove_department_name_remove_discipline__class_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='class', - name='classroom', - field=models.CharField(max_length=60), - ), - migrations.AlterField( - model_name='class', - name='days', - field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=60), size=None), - ), - migrations.AlterField( - model_name='class', - name='schedule', - field=models.CharField(max_length=60), - ), - migrations.AlterField( - model_name='class', - name='teachers', - field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=100), size=None), - ), - migrations.AlterField( - model_name='discipline', - name='name', - field=models.CharField(max_length=100), - ), - ] diff --git a/api/api/migrations/0004_alter_class__class.py b/api/api/migrations/0004_alter_class__class.py deleted file mode 100644 index 444e0685..00000000 --- a/api/api/migrations/0004_alter_class__class.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.5 on 2023-11-06 13:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0003_alter_class_classroom_alter_class_days_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='class', - name='_class', - field=models.CharField(max_length=30), - ), - ] diff --git a/api/api/migrations/0005_alter_department_code.py b/api/api/migrations/0005_alter_department_code.py deleted file mode 100644 index 0500dc00..00000000 --- a/api/api/migrations/0005_alter_department_code.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.5 on 2023-11-06 14:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0004_alter_class__class'), - ] - - operations = [ - migrations.AlterField( - model_name='department', - name='code', - field=models.CharField(max_length=20, unique=True), - ), - ] diff --git a/api/api/migrations/0006_alter_department_code_alter_discipline_code.py b/api/api/migrations/0006_alter_department_code_alter_discipline_code.py deleted file mode 100644 index aeeb670f..00000000 --- a/api/api/migrations/0006_alter_department_code_alter_discipline_code.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.5 on 2023-11-06 14:07 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0005_alter_department_code'), - ] - - operations = [ - migrations.AlterField( - model_name='department', - name='code', - field=models.CharField(max_length=10, unique=True), - ), - migrations.AlterField( - model_name='discipline', - name='code', - field=models.CharField(max_length=20, unique=True), - ), - ] diff --git a/api/api/migrations/0007_alter_department_code_alter_discipline_code.py b/api/api/migrations/0007_alter_department_code_alter_discipline_code.py deleted file mode 100644 index 304bf812..00000000 --- a/api/api/migrations/0007_alter_department_code_alter_discipline_code.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.2.5 on 2023-11-06 14:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0006_alter_department_code_alter_discipline_code'), - ] - - operations = [ - migrations.AlterField( - model_name='department', - name='code', - field=models.CharField(max_length=10), - ), - migrations.AlterField( - model_name='discipline', - name='code', - field=models.CharField(max_length=20), - ), - ] From 12ad40166df73d9d0348e5951cdbd94dc096d61b Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 6 Nov 2023 13:57:25 -0300 Subject: [PATCH 27/33] db(handler): remove duplicated file --- api/api/utils/db_handle.py | 36 ------------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 api/api/utils/db_handle.py diff --git a/api/api/utils/db_handle.py b/api/api/utils/db_handle.py deleted file mode 100644 index af496711..00000000 --- a/api/api/utils/db_handle.py +++ /dev/null @@ -1,36 +0,0 @@ -from api.models import Discipline, Department, Class - -""" Este módulo lida com as operações de banco de dados.""" - -"""Cria um departamento.""" -def create_department(code: str, year: str, period: str) -> Department: - - return Department.objects.get_or_create(code=code, year=year, period=period)[0] - -"""Cria uma disciplina.""" -def create_discipline(name: str, code: str, department: Department) -> Discipline: - - return Discipline.objects.get_or_create(name=name, code=code, department=department)[0] - -"""Cria uma turma de uma disciplina.""" -def create_class(workload: int, teachers: list, classroom: str, schedule: str, - days: list, _class: int, discipline: Discipline) -> Class: - - return Class.objects.create(workload=workload, teachers=teachers, classroom=classroom, schedule=schedule, - days=days, _class=_class, discipline=discipline) - - - - - - -def delete_classes_from_discipline(discipline: Discipline) -> None: - """Deleta todas as turmas de uma disciplina.""" - Class.objects.filter(discipline=discipline).delete() - -def delete_all_departments_using_year_and_period(year: str, period: str) -> None: - """Deleta um departamento de um periodo especifico.""" - Department.objects.filter(year=year, period=period).delete() - - - From 4dcab54a07a7283f26785d98d71ba99605adedb6 Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 6 Nov 2023 13:57:52 -0300 Subject: [PATCH 28/33] db(updatedb.py): update print messages --- api/api/management/commands/updatedb.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/api/api/management/commands/updatedb.py b/api/api/management/commands/updatedb.py index 2b547fdb..1c82f382 100644 --- a/api/api/management/commands/updatedb.py +++ b/api/api/management/commands/updatedb.py @@ -18,6 +18,8 @@ def handle(self, *args: Any, **options: Any): self.display_error_message("department_ids") return + print("Atualizando o banco de dados...") + # Obtem o ano e o período atual e o ano e o período seguinte previous_period_year, previous_period = sessions.get_previous_period() current_year, current_period = sessions.get_current_year_and_period() @@ -37,12 +39,12 @@ def handle(self, *args: Any, **options: Any): # Atualiza as disciplinas do período atual start_time = time() self.update_departments(departments_ids=departments_ids, year=current_year, period=current_period) - self.display_success_message(operation="atualização de departamentos e matérias do período atual", start_time=start_time) + self.display_success_message(operation=f"{current_year}/{current_period}", start_time=start_time) # Atualiza as disciplinas do período seguinte start_time = time() self.update_departments(departments_ids=departments_ids, year=next_period_year, period=next_period) - self.display_success_message(operation="atualização de departamentos e matérias", start_time=start_time) + self.display_success_message(operation=f"{next_period_year}/{next_period}", start_time=start_time) def update_departments(self, departments_ids: list, year: str, period: str) -> None: for department_id in departments_ids: @@ -67,9 +69,9 @@ def update_departments(self, departments_ids: list, year: str, period: str) -> N def display_error_message(self, operation: str) -> None: print("Não foi possível realizar a operação de atualização do banco de dados.") print("Verifique se o SIGAA está funcionando corretamente.") - print(f"Falha em {operation}") + print(f"Falha em {operation}", end="\n\n") def display_success_message(self, operation: str, start_time: float) -> None: print("Operação de atualização do banco de dados realizada com sucesso.") print(f"Sucesso em {operation}") - print(f"Tempo de execução: {(time() - start_time):.1f}s") + print(f"Tempo de execução: {(time() - start_time):.1f}s", end="\n\n") From 1b637b9e23cdd1091f3f58d6a266e3ad9a060d0b Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 6 Nov 2023 14:05:25 -0300 Subject: [PATCH 29/33] db(updatedb): remove duplicated code --- api/api/management/commands/updatedb.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/api/api/management/commands/updatedb.py b/api/api/management/commands/updatedb.py index 1c82f382..4205d921 100644 --- a/api/api/management/commands/updatedb.py +++ b/api/api/management/commands/updatedb.py @@ -28,14 +28,6 @@ def handle(self, *args: Any, **options: Any): # Apaga as disciplinas do período interior db_handler.delete_all_departments_using_year_and_period(previous_period_year, previous_period) - # Pega a lista de departamentos do web scraping - departments_ids = web_scraping.get_list_of_departments() - - # Se não for possível obter os códigos dos departamentos, exibe uma mensagem de erro - if departments_ids is None: - self.display_error_message("department_ids") - return - # Atualiza as disciplinas do período atual start_time = time() self.update_departments(departments_ids=departments_ids, year=current_year, period=current_period) From dd86d33d70e63d1a2f4b3fa2bfb589e4cd25752a Mon Sep 17 00:00:00 2001 From: Mateus Vieira <68292695+mateusvrs@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:17:03 -0300 Subject: [PATCH 30/33] git(ignored): sync gitignore com a branch main --- .gitignore | 216 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 144 insertions(+), 72 deletions(-) diff --git a/.gitignore b/.gitignore index abba5ac0..004f7a8c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,56 +1,31 @@ -# Django # +# Created by https://www.toptal.com/developers/gitignore/api/django,nextjs +# Edit at https://www.toptal.com/developers/gitignore?templates=django,nextjs + +### Django ### *.log *.pot *.pyc -__pycache__ +__pycache__/ +local_settings.py db.sqlite3 +db.sqlite3-journal media -# Backup files # -*.bak - -# If you are using PyCharm # -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# File-based project format -*.iws - -# IntelliJ -out/ - -# JIRA plugin -atlassian-ide-plugin.xml +# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ +# in your Git repository. Update and uncomment the following line accordingly. +# /staticfiles/ -# Python # +### Django.Python Stack ### +# Byte-compiled / optimized / DLL files *.py[cod] *$py.class +# C extensions +*.so + # Distribution / packaging -.Python build/ +.Python +build/ develop-eggs/ dist/ downloads/ @@ -62,9 +37,15 @@ parts/ sdist/ var/ wheels/ +share/python-wheels/ *.egg-info/ .installed.cfg *.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec @@ -75,23 +56,77 @@ pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ +.nox/ .coverage .coverage.* .cache -.pytest_cache/ nosetests.xml coverage.xml *.cover +*.py,cover .hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo + +# Django stuff: + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ # Jupyter Notebook .ipynb_checkpoints -# pyenv -.python-version +# IPython +profile_default/ +ipython_config.py -# celery -celerybeat-schedule.* +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid # SageMath parsed files *.sage.py @@ -105,34 +140,71 @@ ENV/ env.bak/ venv.bak/ +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + # mkdocs documentation /site # mypy .mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### NextJS ### +# dependencies +node_modules/ +/.pnp +.pnp.js + +# testing +/coverage + +# next.js +.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts -# Sublime Text # -*.tmlanguage.cache -*.tmPreferences.cache -*.stTheme.cache -*.sublime-workspace -*.sublime-project - -# sftp configuration file -sftp-config.json - -# Package control specific files Package -Control.last-run -Control.ca-list -Control.ca-bundle -Control.system-ca-bundle -GitHub.sublime-settings - -# Visual Studio Code # -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history -.vscode/settings.json +# End of https://www.toptal.com/developers/gitignore/api/django,nextjs From cb650a2f35bb0f614133948481ebda0657513cd6 Mon Sep 17 00:00:00 2001 From: mateuvrs Date: Mon, 6 Nov 2023 14:34:41 -0300 Subject: [PATCH 31/33] =?UTF-8?q?api(admin):=20adiciona=20e=20config=20as?= =?UTF-8?q?=20models=20a=20p=C3=A1gina?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/api/admin.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/api/api/admin.py b/api/api/admin.py index 8c38f3f3..a1a55714 100644 --- a/api/api/admin.py +++ b/api/api/admin.py @@ -1,3 +1,23 @@ from django.contrib import admin +from .models import Department, Discipline, Class -# Register your models here. + +@admin.register(Department) +class DepartmentAdmin(admin.ModelAdmin): + list_display = ['code', 'year', 'period'] + search_fields = ['code'] + ordering = ['year', 'period'] + + +@admin.register(Discipline) +class DisciplineAdmin(admin.ModelAdmin): + list_display = ['name', 'code'] + search_fields = ['name', 'code'] + ordering = ['name'] + + +@admin.register(Class) +class ClassAdmin(admin.ModelAdmin): + list_display = ['discipline', 'classroom', 'schedule'] + search_fields = ['discipline__name'] + ordering = ['discipline__name'] From f83b017c6acbc46c95fa84fc1b77cf6af3e00d17 Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 6 Nov 2023 14:55:33 -0300 Subject: [PATCH 32/33] db(updatedb): remove useless imports --- api/api/management/commands/updatedb.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/api/api/management/commands/updatedb.py b/api/api/management/commands/updatedb.py index 4205d921..566d5be7 100644 --- a/api/api/management/commands/updatedb.py +++ b/api/api/management/commands/updatedb.py @@ -1,11 +1,8 @@ from typing import Any from django.core.management.base import BaseCommand from api.utils import sessions, web_scraping, db_handler -from api.models import Discipline, Department -from decouple import config from time import time - class Command(BaseCommand): """Comando para atualizar o banco de dados.""" From 15b7e5a7a9c607429b3f7db4346c27ab6fcc9986 Mon Sep 17 00:00:00 2001 From: Caio Date: Mon, 6 Nov 2023 15:03:45 -0300 Subject: [PATCH 33/33] db(db_handler): refactor functions names --- api/api/management/commands/updatedb.py | 4 ++-- api/api/utils/db_handler.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/api/management/commands/updatedb.py b/api/api/management/commands/updatedb.py index 566d5be7..8c178f66 100644 --- a/api/api/management/commands/updatedb.py +++ b/api/api/management/commands/updatedb.py @@ -38,14 +38,14 @@ def handle(self, *args: Any, **options: Any): def update_departments(self, departments_ids: list, year: str, period: str) -> None: for department_id in departments_ids: disciplines_list = web_scraping.get_department_disciplines(department_id=department_id, current_year=year, current_period=period) - department = db_handler.create_department(code=department_id, year=year, period=period) + department = db_handler.get_or_create_department(code=department_id, year=year, period=period) # Para cada disciplina do período atual, deleta as turmas previamente cadastradas e cadastra novas turmas no banco de dados for discipline_code in disciplines_list: classes_info = disciplines_list[discipline_code] # Cria ou pega a disciplina - discipline = db_handler.create_discipline(name=classes_info[0]["name"], code=discipline_code, department=department) + discipline = db_handler.get_or_create_discipline(name=classes_info[0]["name"], code=discipline_code, department=department) # Deleta as turmas previamente cadastradas db_handler.delete_classes_from_discipline(discipline=discipline) diff --git a/api/api/utils/db_handler.py b/api/api/utils/db_handler.py index d8ee41eb..c87b4557 100644 --- a/api/api/utils/db_handler.py +++ b/api/api/utils/db_handler.py @@ -3,12 +3,12 @@ """ Este módulo lida com as operações de banco de dados.""" -def create_department(code: str, year: str, period: str) -> Department: +def get_or_create_department(code: str, year: str, period: str) -> Department: """Cria um departamento.""" return Department.objects.get_or_create(code=code, year=year, period=period)[0] -def create_discipline(name: str, code: str, department: Department) -> Discipline: +def get_or_create_discipline(name: str, code: str, department: Department) -> Discipline: """Cria uma disciplina.""" return Discipline.objects.get_or_create(name=name, code=code, department=department)[0]