Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert "api(search): corrige experiência do usuário e adiciona verificações" #121

Merged
merged 1 commit into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 5 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

# Configuration
config: copy-env setup-env config-mock entrypoint-chmod
copy-env:
copy-env:
cp ./api/.env.example ./api/.env
cp ./web/.env.example ./web/.env.local

setup-env:
setup-env:
bash scripts/env.sh

config-mock:
Expand All @@ -24,7 +24,7 @@ install:
## Docker ##

start:
sudo docker compose up -d
sudo docker compose up -d

start-b:
sudo docker compose up --build -d
Expand All @@ -33,7 +33,7 @@ stop:
sudo docker compose down

stop-v:
sudo docker compose down -v
sudo docker compose down -v


## Django Shortcuts ##
Expand All @@ -42,7 +42,7 @@ stop-v:
test:
python3 scripts/test.py --clean

testfull:
testfull:
python3 scripts/test.py
sudo docker exec django-api coverage html
python3 scripts/report.py
Expand All @@ -57,9 +57,3 @@ makemigrations:

migrate:
sudo docker exec django-api python3 manage.py migrate

# Database Operations
updatedb-all:
sudo docker exec django-api python3 manage.py updatedb -a
deletedb-all:
sudo docker exec django-api python3 manage.py updatedb -d -a
24 changes: 12 additions & 12 deletions api/api/management/commands/updatedb.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ def add_arguments(self, parser: CommandParser) -> None:
"""Adiciona os argumentos do comando."""
parser.add_argument('-a', '-all', action='store_true', dest='all', default=False,
help="Atualiza o banco de dados com as disciplinas dos períodos atual e seguinte.")

parser.add_argument('-p', '--period', action='store', default=None,
parser.add_argument('-p', '--period', action='store', default=None,
choices=[".".join(sessions.get_current_year_and_period()), ".".join(sessions.get_next_period())],
dest='period', help="Atualiza o banco de dados com as disciplinas do período especificado.")

Expand All @@ -35,27 +35,27 @@ def handle(self, *args: Any, **options: Any):
print("Nenhum período foi especificado.")
print("Utilize o comando 'updatedb -h' para mais informações.")
return

# Obtem o ano e o período anterior ao período atual
# 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()

# Apaga as disciplinas do período anterior
# Apaga as disciplinas do período interior
delete_all_departments_using_year_and_period(
year=previous_period_year, period=previous_period)

if options["delete"]:
for year, period in choices:
self.delete_period(year=year, period=period)
return

departments_ids = web_scraping.get_list_of_departments()

if departments_ids is None:
self.display_error_message("department_ids")
return

print("Atualizando o banco de dados...")

for year, period in choices:
start_time = time()
self.update_departments(departments_ids=departments_ids, year=year, period=period)
Expand All @@ -68,14 +68,14 @@ def update_departments(self, departments_ids: list, year: str, period: str) -> N
department_id=department_id, current_year=year, current_period=period)
department = 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 = get_or_create_discipline(
name=classes_info[0]["name"], code=discipline_code, department=department)

# Deleta as turmas previamente cadastradas
delete_classes_from_discipline(discipline=discipline)

Expand Down Expand Up @@ -104,5 +104,5 @@ def display_success_update_message(self, operation: str, start_time: float) -> N

def display_success_delete_message(self, operation: str, start_time: float) -> None:
print("Operação de remoção do banco de dados realizada com sucesso.")
print(f"Sucesso em {operation}")
print(f"Tempo de execução: {(time() - start_time):.1f}s\n")
print(f"Sucesso em {operation}")
print(f"Tempo de execução: {(time() - start_time):.1f}s\n")
22 changes: 0 additions & 22 deletions api/api/migrations/0004_auto_20231126_1740.py

This file was deleted.

18 changes: 0 additions & 18 deletions api/api/migrations/0005_discipline_unicode_name.py

This file was deleted.

14 changes: 0 additions & 14 deletions api/api/migrations/0006_merge_20231127_2255.py

This file was deleted.

7 changes: 0 additions & 7 deletions api/api/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from django.db import models
from unidecode import unidecode
from django.contrib.postgres.fields import ArrayField

class Department(models.Model):
Expand All @@ -18,21 +17,15 @@ def __str__(self):
class Discipline(models.Model):
"""Classe que representa uma disciplina.
name:str -> Nome da disciplina
unicode_name:str -> Nome da disciplina normalizado
code:str -> Código da disciplina
department:Department -> Departamento da disciplina
"""
name = models.CharField(max_length=128)
unicode_name = models.CharField(max_length=128, default='')
code = models.CharField(max_length=64)
department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='disciplines')

def __str__(self):
return self.name

def save(self, *args, **kwargs):
self.unicode_name = unidecode(self.name).casefold()
super(Discipline, self).save(*args, **kwargs)

class Class(models.Model):
"""Classe que representa uma turma.
Expand Down
77 changes: 61 additions & 16 deletions api/api/tests/test_search_api.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from rest_framework.test import APITestCase
from utils.db_handler import get_or_create_department, get_or_create_discipline, create_class
from api.views import ERROR_MESSAGE, ERROR_MESSAGE_SEARCH_LENGTH
from api.views import ERROR_MESSAGE
import json

class TestSearchAPI(APITestCase):
Expand All @@ -16,6 +16,42 @@ def setUp(self) -> None:
self._class_2 = create_class(teachers=['VINICIUS RISPOLI'], classroom='S1', schedule='24M34', days=[
'Segunda-Feira 10:00 às 11:50', 'Quarta-Feira 10:00 às 11:50'], _class="1", special_dates=[], discipline=self.discipline_2)

def test_with_complete_correct_search(self):
"""
Testa a busca por disciplinas com o nome completo e todos os parâmetros corretos

Testes:
- Status code (200 OK)
- Quantidade de disciplinas retornadas
- Código do departamento
- Nome da disciplina
- Professores da disciplina
"""
response_for_discipline_1 = self.client.get(
'/courses/?search=calculo+1&year=2023&period=2')
response_for_discipline_2 = self.client.get(
'/courses/?search=calculo+2&year=2023&period=2')
content_1 = json.loads(response_for_discipline_1.content)
content_2 = json.loads(response_for_discipline_2.content)

# Testes da disciplina 1
self.assertEqual(response_for_discipline_1.status_code, 200)
self.assertEqual(len(content_1), 1)
self.assertEqual(content_1[0]['department']
['code'], self.department.code)
self.assertEqual(content_1[0]['name'], self.discipline_1.name)
self.assertEqual(content_1[0]['classes'][0]
['teachers'], self._class_1.teachers)

# Testes da disciplina 2
self.assertEqual(response_for_discipline_2.status_code, 200)
self.assertEqual(len(content_2), 1)
self.assertEqual(content_2[0]['department']
['code'], self.department.code)
self.assertEqual(content_2[0]['name'], self.discipline_2.name)
self.assertEqual(content_2[0]['classes'][0]
['teachers'], self._class_2.teachers)

def test_with_incomplete_correct_search(self):
"""
Testa a busca por disciplinas com nome incompleto e todos os parâmetros corretos
Expand Down Expand Up @@ -68,6 +104,29 @@ def test_with_code_search(self):
# Testes da disciplina 2
self.assertEqual(response_for_discipline_2.status_code, 200)
self.assertEqual(len(content_2), 1)

def test_with_code_search_spaced(self):
"""
Testa a busca por disciplinas através do código da matéria com espaços
Testes:
- Status code (200 OK)
- Quantidade de disciplinas retornadas
"""

response_for_discipline_1 = self.client.get(
'/courses/?search=MAT+518&year=2023&period=2')
response_for_discipline_2 = self.client.get(
'/courses/?search=MAT+519&year=2023&period=2')
content_1 = json.loads(response_for_discipline_1.content)
content_2 = json.loads(response_for_discipline_2.content)

# Testes da disciplina 1
self.assertEqual(response_for_discipline_1.status_code, 200)
self.assertEqual(len(content_1), 1)

# Testes da disciplina 2
self.assertEqual(response_for_discipline_2.status_code, 200)
self.assertEqual(len(content_2), 1)

def test_with_bad_url_search_missing_year(self):
"""
Expand Down Expand Up @@ -191,18 +250,4 @@ def test_with_only_spaces(self):

self.assertEqual(response_3.status_code, 400)
self.assertEqual(len(content_3), 1)
self.assertEqual(content_3['errors'], ERROR_MESSAGE)

def test_with_insufficient_search_length(self):
"""
Testa a busca por disciplinas com menos de 4 caracteres no parâmetro de busca
Testes:
- Status code (400 BAD REQUEST)
"""

response_1 = self.client.get('/courses/?search=cal&year=2023&period=2')
content_1 = json.loads(response_1.content)

self.assertEqual(response_1.status_code, 400)
self.assertEqual(len(content_1), 1)
self.assertEqual(content_1['errors'], ERROR_MESSAGE_SEARCH_LENGTH)
self.assertEqual(content_3['errors'], ERROR_MESSAGE)
42 changes: 7 additions & 35 deletions api/api/views.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
from utils.db_handler import filter_disciplines_by_year_and_period, get_best_similarities_by_name, filter_disciplines_by_code
from .models import Discipline
from unidecode import unidecode
from django.contrib import admin
from django.db.models.query import QuerySet
from utils.db_handler import filter_disciplines_by_name, filter_disciplines_by_code, filter_disciplines_by_year_and_period
from rest_framework.decorators import APIView
from .serializers import DisciplineSerializer
from utils.sessions import get_current_year_and_period, get_next_period
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework import status

MAXIMUM_RETURNED_DISCIPLINES = 8
MAXIMUM_RETURNED_DISCIPLINES = 5
ERROR_MESSAGE = "no valid argument found for 'search', 'year' or 'period'"
MINIMUM_SEARCH_LENGTH = 4
ERROR_MESSAGE_SEARCH_LENGTH = f"search must have at least {MINIMUM_SEARCH_LENGTH} characters"


class Search(APIView):
Expand All @@ -23,51 +17,29 @@ def treat_string(self, string: str | None) -> str | None:

return string


def filter_disciplines(self, request: Request, name: str) -> QuerySet[Discipline]:
unicode_name = unidecode(name).casefold()

model_handler = admin.ModelAdmin(Discipline, admin.site)
model_handler.search_fields = ['unicode_name', 'code']

disciplines = Discipline.objects.all()
disciplines, _ = model_handler.get_search_results(
request, disciplines, unicode_name)

return disciplines

def get(self, request: Request, *args, **kwargs) -> Response:
name = self.treat_string(request.GET.get('search', None))
year = self.treat_string(request.GET.get('year', None))
period = self.treat_string(request.GET.get('period', None))

name_verified = name is not None and len(name) > 0
year_verified = year is not None and len(year) > 0
period_verified = period is not None and len(period) > 0

if not name_verified or not year_verified or not period_verified:
if name is None or len(name) == 0 or year is None or len(year) == 0 or period is None or len(period) == 0:
return Response(
{
"errors": ERROR_MESSAGE
}, status.HTTP_400_BAD_REQUEST)

if len(name) < MINIMUM_SEARCH_LENGTH:
return Response(
{
"errors": ERROR_MESSAGE_SEARCH_LENGTH
}, status.HTTP_400_BAD_REQUEST)
name = name.split()
disciplines = filter_disciplines_by_name(name=name[0])

disciplines = self.filter_disciplines(request, name)
disciplines = get_best_similarities_by_name(name, disciplines)
for term in name[1:]:
disciplines &= filter_disciplines_by_name(name=term)

if not disciplines.count():
disciplines = filter_disciplines_by_code(code=name[0])

for term in name[1:]:
disciplines &= filter_disciplines_by_code(code=term)

disciplines = filter_disciplines_by_code(name)

filtered_disciplines = filter_disciplines_by_year_and_period(
year=year, period=period, disciplines=disciplines)
data = DisciplineSerializer(filtered_disciplines, many=True).data
Expand Down
Loading