Skip to content

Commit

Permalink
task(schedule/save): criar a rota de salvamento de grade p/usuários a…
Browse files Browse the repository at this point in the history
…utênticados (#144)

* api(save-schedule): Created models and url path.

- Still working on views

* api(models): add migrations para o modelo d/grades

* api(urls): arruma rota da view de salvamento

* api(views): organiza imports por grupos de lib

Co-authored-by: GabrielCastelo-31 <[email protected]>
Co-authored-by: Caio <[email protected]>
Co-authored-by: Arthur Ribeiro <[email protected]>

* api(settings): desliga adição de slash

Co-authored-by: GabrielCastelo-31 <[email protected]>
Co-authored-by: Caio <[email protected]>
Co-authored-by: Arthur Ribeiro <[email protected]>

* api(utils): funcs p/salvar grade horária no db

Co-authored-by: GabrielCastelo-31 <[email protected]>
Co-authored-by: Caio <[email protected]>
Co-authored-by: Arthur Ribeiro <[email protected]>

* api(views): modulariza views em uma pasta separada

Co-authored-by: GabrielCastelo-31 <[email protected]>
Co-authored-by: Caio <[email protected]>
Co-authored-by: Arthur Ribeiro <[email protected]>

* api(views): fnc p/verificar e salvar grade do user

Co-authored-by: GabrielCastelo-31 <[email protected]>
Co-authored-by: Caio <[email protected]>
Co-authored-by: Arthur Ribeiro <[email protected]>

* api(urls): adição da view nas rotas e no admin

Co-authored-by: GabrielCastelo-31 <[email protected]>
Co-authored-by: Caio <[email protected]>
Co-authored-by: Arthur Ribeiro <[email protected]>

* api(settings): definição da auth padrão do swagger

Co-authored-by: GabrielCastelo-31 <[email protected]>
Co-authored-by: Caio <[email protected]>
Co-authored-by: Arthur Ribeiro <[email protected]>

* api(views): correção do parm status de retorno da

* api(utils): add linha de no cover p/tests

* refactor(api): add espaço entre classe e função

* tests(api): verifica as fnc do salvamento de grade

- Tests para os casos de sucesso e erro do salvamento de grade
- Verificação da models e do método __str__ da grade

* refactor(views): tenta diminuir cog complexidade

* refactor(views): diminui cog complexidade

- A função de gerar estava com uma complexidade alta
assim foi necessário refatorar para diminuir a complexidade

* api(admin): deixa json bonito na pág admin

* api(views): adiciona validador de request body

* tests(api): verifica os casos de body incorreto

* refactor(api/views): diminui cog complexidade

* refactor(api): reduz cong complexidade do checker

* refactor(api): modulariza validade class func

---------

Co-authored-by: GabrielCastelo-31 <[email protected]>
Co-authored-by: Caio <[email protected]>
Co-authored-by: Arthur Ribeiro <[email protected]>
  • Loading branch information
4 people authored Dec 8, 2023
1 parent 5945074 commit 39c7d1a
Show file tree
Hide file tree
Showing 17 changed files with 883 additions and 35 deletions.
17 changes: 16 additions & 1 deletion api/api/admin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.contrib import admin
from .models import Department, Discipline, Class
from .models import Department, Discipline, Class, Schedule

from utils.json_pretty import json_prettify


@admin.register(Department)
Expand All @@ -21,3 +23,16 @@ class ClassAdmin(admin.ModelAdmin):
list_display = ['discipline', 'classroom', 'schedule']
search_fields = ['discipline__name']
ordering = ['discipline__name']


@admin.register(Schedule)
class ScheduleAdmin(admin.ModelAdmin):
list_display = ['id', 'user']
exclude = ('classes', )
readonly_fields = ('classes_pretty', )
ordering = ['id']

def classes_pretty(self, obj):
return json_prettify(obj.classes)

classes_pretty.short_description = 'Classes'
24 changes: 24 additions & 0 deletions api/api/migrations/0007_schedule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 4.2.5 on 2023-12-06 23:40

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('api', '0006_merge_20231127_2255'),
]

operations = [
migrations.CreateModel(
name='Schedule',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('classes', models.JSONField(default=list)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='schedules', to=settings.AUTH_USER_MODEL)),
],
),
]
26 changes: 23 additions & 3 deletions api/api/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from django.db import models
from unidecode import unidecode
from django.contrib.postgres.fields import ArrayField
from users.models import User


class Department(models.Model):
"""Classe que representa um departamento.
Expand All @@ -15,6 +17,7 @@ class Department(models.Model):
def __str__(self):
return self.code


class Discipline(models.Model):
"""Classe que representa uma disciplina.
name:str -> Nome da disciplina
Expand All @@ -25,15 +28,17 @@ class Discipline(models.Model):
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')
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.
teachers:list -> Lista de professores da turma
Expand All @@ -48,7 +53,9 @@ class Class(models.Model):
schedule = models.CharField(max_length=512)
days = ArrayField(models.CharField(max_length=64))
_class = models.CharField(max_length=64)
discipline = models.ForeignKey(Discipline, on_delete=models.CASCADE, related_name='classes')
discipline = models.ForeignKey(
Discipline, on_delete=models.CASCADE, related_name='classes')

special_dates = ArrayField(
ArrayField(
models.CharField(max_length=256),
Expand All @@ -59,3 +66,16 @@ class Class(models.Model):

def __str__(self):
return self._class


class Schedule(models.Model):
"""Classe que representa um horário.
user:User -> Usuário do horário
classes:list -> Lista de turmas do horário
"""
user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name='schedules')
classes = models.JSONField(default=list)

def __str__(self):
return f'Class: {self.id} - User: {self.user.email}'
3 changes: 2 additions & 1 deletion api/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ class Meta:


class DisciplineSerializerSchedule(ModelSerializer):
department = DepartmentSerializer()

class Meta:
model = Discipline
fields = '__all__'


class DisciplineSerializer(DisciplineSerializerSchedule):
department = DepartmentSerializer()
classes = ClassSerializer(many=True)


Expand Down
169 changes: 169 additions & 0 deletions api/api/tests/test_error_request_body_schedule_save.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
class ErrorRequestBodyScheduleSave():

def test_save_incorrect_schedule_with_empty_request_body(self):
"""
Testa o salvamento de uma grade horária com um corpo de requisição vazio.
Tests:
- Mensagem de erro
- Status code (400 BAD REQUEST)
"""
response = self.make_post_request()

error_msg = 'the request body must not be empty'
self.assertEqual(response.data.get('errors'), error_msg)
self.assertEqual(response.status_code, 400)

def test_save_incorrect_schedule_with_non_list_request_body(self):
"""
Testa o salvamento de uma grade horária com um corpo de requisição que não é uma lista.
Tests:
- Mensagem de erro
- Status code (400 BAD REQUEST)
"""
response = self.make_post_request(schedule="""{
"hey": "there"
}""")

error_msg = 'the request body must be a list of classes'
self.assertEqual(response.data.get('errors'), error_msg)
self.assertEqual(response.status_code, 400)

def test_save_incorrect_schedule_with_non_dict_class(self):
"""
Testa o salvamento de uma grade horária com uma turma que não é um dicionário.
Tests:
- Mensagem de erro
- Status code (400 BAD REQUEST)
"""
response = self.make_post_request(schedule=[1, 2, 3])

error_msg = 'each class must be a object structure'
self.assertEqual(response.data.get('errors'), error_msg)
self.assertEqual(response.status_code, 400)

def test_save_incorrect_schedule_with_no_discipline_key(self):
"""
Testa o salvamento de uma grade horária com uma turma que não tem a chave "discipline".
Tests:
- Mensagem de erro
- Status code (400 BAD REQUEST)
"""
response = self.make_post_request(schedule="""[{
"schedule": "35T23",
"class": "1",
"teachers": ["EDSON ALVES DA COSTA JUNIOR"],
"classroom": "FGA - I8",
"days": ["Terça-feira 14:00 às 15:50", "Quinta-feira 14:00 às 15:50"],
"special_dates": []
}]""")

error_msg = 'the class must have the discipline key'
self.assertEqual(response.data.get('errors'), error_msg)
self.assertEqual(response.status_code, 400)

def test_save_incorrect_schedule_with_non_dict_discipline(self):
"""
Testa o salvamento de uma grade horária com uma disciplina que não é um dicionário.
Tests:
- Mensagem de erro
- Status code (400 BAD REQUEST)
"""
response = self.make_post_request(schedule="""[{
"discipline": 1,
"schedule": "35T23",
"class": "1",
"teachers": ["EDSON ALVES DA COSTA JUNIOR"],
"classroom": "FGA - I8",
"days": ["Terça-feira 14:00 às 15:50", "Quinta-feira 14:00 às 15:50"],
"special_dates": []
}]""")

error_msg = 'the discipline must be a object structure'
self.assertEqual(response.data.get('errors'), error_msg)
self.assertEqual(response.status_code, 400)

def test_save_incorrect_schedule_with_no_discipline_name_key(self):
"""
Testa o salvamento de uma grade horária com uma disciplina que não tem a chave "name".
Tests:
- Mensagem de erro
- Status code (400 BAD REQUEST)
"""
response = self.make_post_request(schedule="""[{
"discipline": {},
"schedule": "35T23",
"class": "1",
"teachers": ["EDSON ALVES DA COSTA JUNIOR"],
"classroom": "FGA - I8",
"days": ["Terça-feira 14:00 às 15:50", "Quinta-feira 14:00 às 15:50"],
"special_dates": []
}]""")

error_msg = 'the discipline must have the name key'
self.assertEqual(response.data.get('errors'), error_msg)
self.assertEqual(response.status_code, 400)

def test_save_incorrect_schedule_with_no_dict_discipline_department(self):
"""
Testa o salvamento de uma grade horária com uma disciplina que não tem a chave "department".
Tests:
- Mensagem de erro
- Status code (400 BAD REQUEST)
"""
response = self.make_post_request(schedule="""[{
"discipline": {
"name": "CÁLCULO 1",
"code": "MAT0025",
"department": []
},
"schedule": "35T23",
"class": "1",
"teachers": ["EDSON ALVES DA COSTA JUNIOR"],
"classroom": "FGA - I8",
"days": ["Terça-feira 14:00 às 15:50", "Quinta-feira 14:00 às 15:50"],
"special_dates": []
}]""")

error_msg = 'the department must be a object structure'
self.assertEqual(response.data.get('errors'), error_msg)
self.assertEqual(response.status_code, 400)

def test_save_incorrect_schedule_with_no_dict_discipline_department_year(self):
"""
Testa o salvamento de uma grade horária com uma disciplina que não tem a chave "year".
Tests:
- Mensagem de erro
- Status code (400 BAD REQUEST)
"""
response = self.make_post_request(schedule="""[{
"discipline": {
"name": "CÁLCULO 1",
"code": "MAT0025",
"department": {
"period": "2"
}
},
"schedule": "35T23",
"class": "1",
"teachers": ["EDSON ALVES DA COSTA JUNIOR"],
"classroom": "FGA - I8",
"days": ["Terça-feira 14:00 às 15:50", "Quinta-feira 14:00 às 15:50"],
"special_dates": []
}]""")

error_msg = 'the department must have the year key'
self.assertEqual(response.data.get('errors'), error_msg)
self.assertEqual(response.status_code, 400)
48 changes: 48 additions & 0 deletions api/api/tests/test_schedule_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from django.test import TestCase

from api.models import Schedule

from users.models import User

import json


class ScheduleModelsTest(TestCase):

def setUp(self):
self.user, _ = User.objects.get_or_create(
first_name="test",
last_name="banana",
picture_url="https://photo.aqui.com",
email="[email protected]",
)
self.user.save()

mock_json = json.dumps([
{'class': 1},
{'class': 2},
])
self.schedule = Schedule.objects.create(
user=self.user,
classes=mock_json
)

def test_create_schedule(self):
"""
Testa se o schedule foi criado corretamente
Tests:
- Se o usuário é o mesmo que o entrega na criação
- Se as classes são as mesmas que as entregues na criação
"""
self.assertEqual(self.schedule.user, self.user)
self.assertEqual(self.schedule.classes, json.dumps([
{'class': 1},
{'class': 2},
]))

def test_str_method_of_schedule(self):
"""
Testa se o método __str__ de Schedule retorna o json correto
"""
self.assertEqual(str(self.schedule), f'Class: {self.schedule.id} - User: {self.user.email}')
Loading

0 comments on commit 39c7d1a

Please sign in to comment.