diff --git a/config/pagination.py b/config/pagination.py new file mode 100644 index 0000000..e0a0b34 --- /dev/null +++ b/config/pagination.py @@ -0,0 +1,25 @@ +from rest_framework.pagination import PageNumberPagination + + +class XSResultsSetPagination(PageNumberPagination): + page_size = 5 + page_size_query_param = 'page_size' + max_page_size = 20 + + +class SMResultsSetPagination(PageNumberPagination): + page_size = 20 + page_size_query_param = 'page_size' + max_page_size = 80 + + +class MDResultsSetPagination(PageNumberPagination): + page_size = 80 + page_size_query_param = 'page_size' + max_page_size = 200 + + +class LGResultsSetPagination(PageNumberPagination): + page_size = 200 + page_size_query_param = 'page_size' + max_page_size = 500 diff --git a/config/settings.py b/config/settings.py index 9837888..991f7e1 100644 --- a/config/settings.py +++ b/config/settings.py @@ -11,6 +11,7 @@ """ import os +import sys # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -127,6 +128,12 @@ } } +if 'test' in sys.argv: + DATABASES['default'] = { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'test_db' + } + # Password validation # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators diff --git a/djangoapps/classes/migrations/0001_initial.py b/djangoapps/classes/migrations/0001_initial.py new file mode 100644 index 0000000..01df30c --- /dev/null +++ b/djangoapps/classes/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 3.0.7 on 2020-07-20 11:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('rooms', '0001_initial'), + ('groups', '0001_initial'), + ('teachers', '0001_initial'), + ('timeslots', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Class', + fields=[ + ('id', models.IntegerField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=200)), + ('type', models.CharField(max_length=10)), + ('day_of_week', models.IntegerField()), + ('week_number', models.IntegerField()), + ('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='classes', to='groups.Group')), + ('room', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='classes', to='rooms.Room')), + ('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='classes', to='teachers.Teacher')), + ('time_slot', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='classes', to='timeslots.TimeSlot')), + ], + options={ + 'db_table': 'class', + }, + ), + ] diff --git a/djangoapps/classes/migrations/0002_class_groups_new.py b/djangoapps/classes/migrations/0002_class_groups_new.py new file mode 100644 index 0000000..c9d35c8 --- /dev/null +++ b/djangoapps/classes/migrations/0002_class_groups_new.py @@ -0,0 +1,32 @@ +# Generated by Django 3.0.7 on 2020-07-20 11:57 + +from django.db import migrations, models +from djangoapps.classes.models import Class + + +def transfer_old_group_relation(apps, schema_editor): + class_model: Class = apps.get_model('classes', 'Class') + for university_class in class_model.objects.all(): + university_class.groups_new.add(university_class.group) + university_class.save() + + +class Migration(migrations.Migration): + """ + Change relation between class-group from many-to-one --> many-to-many + """ + + dependencies = [ + ('groups', '0001_initial'), + ('classes', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='class', + name='groups_new', + field=models.ManyToManyField(related_name='classes_new', to='groups.Group'), + ), + + migrations.RunPython(transfer_old_group_relation), + ] diff --git a/djangoapps/classes/migrations/0003_class_groups_final.py b/djangoapps/classes/migrations/0003_class_groups_final.py new file mode 100644 index 0000000..3c76038 --- /dev/null +++ b/djangoapps/classes/migrations/0003_class_groups_final.py @@ -0,0 +1,23 @@ +# Generated by Django 3.0.7 on 2020-07-22 09:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('groups', '0001_initial'), + ('classes', '0002_class_groups_new'), + ] + + operations = [ + migrations.RemoveField( + model_name='class', + name='group', + ), + migrations.RenameField( + model_name='class', + old_name='groups_new', + new_name='groups', + ), + ] diff --git a/djangoapps/classes/models.py b/djangoapps/classes/models.py index 945724c..d0b8438 100644 --- a/djangoapps/classes/models.py +++ b/djangoapps/classes/models.py @@ -15,7 +15,7 @@ class Class(models.Model): week_number = models.IntegerField() room = models.ForeignKey(Room, related_name="classes", on_delete=models.CASCADE) - group = models.ForeignKey(Group, related_name="classes", on_delete=models.CASCADE) + groups = models.ManyToManyField(Group, related_name="classes") teacher = models.ForeignKey(Teacher, related_name="classes", on_delete=models.CASCADE) time_slot = models.ForeignKey(TimeSlot, related_name="classes", on_delete=models.CASCADE) @@ -26,5 +26,5 @@ def __repr__(self): short_name = self.name[:10] + "..." if len(self.name) > 10 else self.name return f"" diff --git a/djangoapps/classes/serializers.py b/djangoapps/classes/serializers.py index 1eafb5f..316d6cb 100644 --- a/djangoapps/classes/serializers.py +++ b/djangoapps/classes/serializers.py @@ -10,7 +10,7 @@ class ClassSerializer(serializers.ModelSerializer): """ Serializer to represent the Class model """ teacher = TeacherSerializer() room = RoomSerializer() - group = GroupSerializer() + groups = GroupSerializer(many=True, read_only=True) class Meta: model = Class @@ -31,27 +31,28 @@ class Meta: class ClassWithoutRoomSerializer(serializers.ModelSerializer): """ Serializer to represent the Class model without Room """ teacher = TeacherSerializer() - group = GroupSerializer() + groups = GroupSerializer(many=True, read_only=True) class Meta: model = Class fields = ("id", "name", "type", "day_of_week", "week_number", "teacher", - "group", "time_slot") + "groups", "time_slot") -class RawClassSerializer(serializers.ModelSerializer): - """ Serializer to represent raw Class model without teacher, room, group """ +class ClassWithoutTeacherSerializer(serializers.ModelSerializer): + """ Serializer to represent the Class model without Teacher """ + room = RoomSerializer() + groups = GroupSerializer(many=True, read_only=True) class Meta: model = Class - fields = ("id", "name", "type", "day_of_week", "week_number", "time_slot") - + fields = ("id", "name", "type", "day_of_week", "week_number", "room", + "groups", "time_slot") -class RoomsClassesSerializer(serializers.ModelSerializer): - """ Serializer to represent Room model and classes that take place in it """ - classes = RawClassSerializer(many=True) +class RawClassSerializer(serializers.ModelSerializer): + """ Serializer to represent raw Class model without teacher, room, group """ class Meta: - model = Room - fields = ("id", "name", "classes") + model = Class + fields = ("id", "name", "type", "day_of_week", "week_number", "time_slot") diff --git a/djangoapps/classes/tests.py b/djangoapps/classes/tests.py index 7ce503c..c246066 100644 --- a/djangoapps/classes/tests.py +++ b/djangoapps/classes/tests.py @@ -1,3 +1,82 @@ -from django.test import TestCase +import json -# Create your tests here. +from django.urls import reverse +from rest_framework import status +from rest_framework.test import APITestCase + +from djangoapps.tests_data_setup import generate_classes + +from djangoapps.groups.models import Group +from djangoapps.classes.models import Class +from djangoapps.rooms.models import Room +from djangoapps.teachers.models import Teacher + + +class ClassesTest(APITestCase): + + @classmethod + def setUpTestData(cls): + test_group = Group( + id=0, + name='км-92', + ) + Group.save(test_group) + + test_room = Room( + id=0, + name='0', + university_building=0, + ) + Room.save(test_room) + + test_teacher = Teacher( + id=0, + name='teacher', + official_name='off teacher', + ) + Teacher.save(test_teacher) + + classes = generate_classes(1, + groups=[test_group], + rooms=[test_room], + teachers=[test_teacher]) + for test_class in classes: + Class.save(test_class) + + def test_get_classes_by_group_name(self): + """ + Testing ClassesByGroupList view by name. + """ + url = reverse('classes-by-group', kwargs={'group': 'км-92'}) + response = self.client.get(url, format='json') + self.default_classes_assert(response) + + def test_get_classes_by_group_id(self): + """ + Testing ClassesByGroupList view by id. + """ + url = reverse('classes-by-group', kwargs={'group': 0}) + response = self.client.get(url, format='json') + self.default_classes_assert(response) + + def test_get_classes_by_room_id(self): + """ + Testing ClassesByRoom view. + """ + url = reverse('classes-by-room', kwargs={'room': 0}) + response = self.client.get(url, format='json') + self.default_classes_assert(response) + + def test_get_classes_by_teacher_id(self): + """ + Testing ClassesByTeacher view. + """ + url = reverse('classes-by-teacher', kwargs={'teacher': 0}) + response = self.client.get(url, format='json') + self.default_classes_assert(response) + + def default_classes_assert(self, resp): + json_response = json.loads(resp.content) + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual(type(json_response), list) + self.assertGreater(len(json_response), 0) diff --git a/djangoapps/classes/urls.py b/djangoapps/classes/urls.py index 821e943..be3e234 100644 --- a/djangoapps/classes/urls.py +++ b/djangoapps/classes/urls.py @@ -2,7 +2,7 @@ from django.conf.urls import url, include from djangoapps.classes.views import ClassViewSet, ClassesByGroupList, \ - ClassesByBuildingList, ClassesByRoom + ClassesByBuildingList, ClassesByRoom, ClassesByTeacher router = routers.DefaultRouter() router.register(r'classes', ClassViewSet, basename='classes') @@ -17,5 +17,8 @@ url(r'^classes/room/(?P\d+)/$', ClassesByRoom.as_view(), name="classes-by-room"), + url(r'^classes/teacher/(?P\d+)/$', + ClassesByTeacher.as_view(), name="classes-by-teacher"), + url(r'', include(router.urls)), ] diff --git a/djangoapps/classes/views.py b/djangoapps/classes/views.py index 729a824..25aaa09 100644 --- a/djangoapps/classes/views.py +++ b/djangoapps/classes/views.py @@ -6,8 +6,7 @@ from djangoapps.classes.models import Class from djangoapps.groups.models import Group from djangoapps.classes.serializers import ClassSerializer, ClassWithoutGroupSerializer, \ - RoomsClassesSerializer, ClassWithoutRoomSerializer -from djangoapps.rooms.models import Room + ClassWithoutRoomSerializer, ClassWithoutTeacherSerializer class ClassViewSet(ReadOnlyModelViewSet): @@ -41,7 +40,9 @@ def get_queryset(self): raise ParseError if group_id is not None: - classes = Class.objects.filter(group_id=group_id).prefetch_related('group', 'room', 'teacher') + classes = Class.objects.filter(groups__id=group_id).\ + distinct('day_of_week', 'week_number', 'time_slot__id').\ + prefetch_related('groups', 'room', 'teacher') if classes: return classes else: @@ -49,36 +50,42 @@ def get_queryset(self): else: try: group: Group = Group.objects.get(name=group_name) - return Class.objects.filter(group_id=group.id) + return Class.objects.filter(groups__id=group.id).\ + distinct('day_of_week', 'week_number', 'time_slot__id').\ + prefetch_related('groups', 'room', 'teacher') except ObjectDoesNotExist: raise NotFound -class ClassesByBuildingList(generics.ListAPIView): +class ClassesByRoom(generics.ListAPIView): """ - A list of classes by rooms in building. + A list of classes by room. """ - serializer_class = RoomsClassesSerializer + serializer_class = ClassWithoutRoomSerializer def get_queryset(self): try: - building: int = int(self.kwargs["building"]) - return Room.objects.filter(university_building=building).prefetch_related("classes") + room_id: int = int(self.kwargs["room"]) + return Class.objects.filter(room_id=room_id).\ + distinct('day_of_week', 'week_number', 'time_slot__id').\ + prefetch_related("teacher", "groups") except ValueError: raise ParseError -class ClassesByRoom(generics.ListAPIView): +class ClassesByTeacher(generics.ListAPIView): """ - A list of classes by room. + A list of classes by teacher. """ - serializer_class = ClassWithoutRoomSerializer + serializer_class = ClassWithoutTeacherSerializer def get_queryset(self): try: - room_id: int = int(self.kwargs["room"]) - return Class.objects.filter(room_id=room_id).prefetch_related("teacher", "group") + teacher_id: int = int(self.kwargs["teacher"]) + return Class.objects.filter(teacher_id=teacher_id).\ + distinct('day_of_week', 'week_number', 'time_slot__id').\ + prefetch_related("room", "groups") except ValueError: raise ParseError diff --git a/djangoapps/groups/migrations/0001_initial.py b/djangoapps/groups/migrations/0001_initial.py new file mode 100644 index 0000000..0e33285 --- /dev/null +++ b/djangoapps/groups/migrations/0001_initial.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.7 on 2020-07-20 11:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Group', + fields=[ + ('id', models.IntegerField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=50)), + ], + options={ + 'db_table': 'group', + 'ordering': ['name'], + }, + ), + ] diff --git a/djangoapps/groups/tests.py b/djangoapps/groups/tests.py index 7ce503c..3f26451 100644 --- a/djangoapps/groups/tests.py +++ b/djangoapps/groups/tests.py @@ -1,3 +1,52 @@ -from django.test import TestCase +import json +from typing import Set -# Create your tests here. +from django.urls import reverse +from rest_framework.test import APITestCase + +from djangoapps.tests_data_setup import generate_classes + +from djangoapps.groups.models import Group +from djangoapps.classes.models import Class + + +class GroupsTest(APITestCase): + + def test_search_by_name(self): + """ + Testing GroupSearchByNameList view. + """ + groups = [ + Group( + id=0, + name='abc' + ), + Group( + id=1, + name='bbc' + ), + Group( + id=2, + name='cbc' + ), + Group( + id=3, + name='abb' + ), + ] + for group in groups: + Group.save(group) + + classes = generate_classes(1, groups=groups) + for test_class in classes: + Class.save(test_class) + + self.assert_search_results('a', {'abc', 'abb'}) + self.assert_search_results('b', {'bbc'}) + self.assert_search_results('c', {'cbc'}) + + def assert_search_results(self, request: str, expected_names: Set[str]): + url = reverse('groups-search', kwargs={'searchRequest': request}) + response = self.client.get(url, format='json') + json_response = json.loads(response.content) + self.assertEqual(set(map(lambda v: v['name'], json_response)), expected_names) diff --git a/djangoapps/rooms/migrations/0001_initial.py b/djangoapps/rooms/migrations/0001_initial.py new file mode 100644 index 0000000..3cc6402 --- /dev/null +++ b/djangoapps/rooms/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 3.0.7 on 2020-07-20 11:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Room', + fields=[ + ('id', models.IntegerField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=30)), + ('university_building', models.IntegerField()), + ], + options={ + 'db_table': 'room', + 'ordering': ('university_building',), + }, + ), + ] diff --git a/djangoapps/rooms/serializers.py b/djangoapps/rooms/serializers.py index b9e3499..648e07c 100644 --- a/djangoapps/rooms/serializers.py +++ b/djangoapps/rooms/serializers.py @@ -20,4 +20,17 @@ class Meta: fields = ("university_building",) def to_representation(self, instance: Room): - return instance.university_building + return instance['university_building'] + + +class EmptyRoomsSerializer(serializers.Serializer): + week_number = serializers.IntegerField() + day_of_week = serializers.IntegerField() + time_slot = serializers.IntegerField() + rooms = RoomSerializer(many=True, read_only=True) + + def update(self, instance, validated_data): + pass + + def create(self, validated_data): + pass diff --git a/djangoapps/rooms/services.py b/djangoapps/rooms/services.py new file mode 100644 index 0000000..899bd1a --- /dev/null +++ b/djangoapps/rooms/services.py @@ -0,0 +1,46 @@ +from djangoapps.classes.models import Class +from djangoapps.rooms.models import Room +from djangoapps.rooms.types import EmptyRooms +from typing import List + + +class FreeRoomsInBuildingService(object): + + @staticmethod + def get_all_empty_rooms(all_classes: List[Class], all_rooms: List[Room]) -> List[EmptyRooms]: + + all_empty_rooms_list: List[EmptyRooms] = list() + for week_number in (1, 2): + for day_of_week in range(1, 7): + for time_slot in range(1, 6): + all_empty_rooms_list.append( + FreeRoomsInBuildingService.get_empty_rooms_at( + all_classes, all_rooms, + week_number, day_of_week, + time_slot + ) + ) + + return all_empty_rooms_list + + @staticmethod + def get_empty_rooms_at(all_classes: List[Class], all_rooms: List[Room], week_number: int, + day_of_week: int, time_slot: int) -> EmptyRooms: + + empty_rooms_list: List[Room] = all_rooms.copy() + for university_class in all_classes: + if university_class.week_number == week_number \ + and university_class.day_of_week == day_of_week \ + and university_class.time_slot_id == time_slot: + + for index, room in enumerate(empty_rooms_list): + if room.id == university_class.room_id: + empty_rooms_list.pop(index) + break + + return EmptyRooms( + week_number=week_number, + day_of_week=day_of_week, + time_slot=time_slot, + rooms=empty_rooms_list + ) diff --git a/djangoapps/rooms/tests.py b/djangoapps/rooms/tests.py index 7ce503c..97fe769 100644 --- a/djangoapps/rooms/tests.py +++ b/djangoapps/rooms/tests.py @@ -1,3 +1,64 @@ -from django.test import TestCase +import json +from typing import Set -# Create your tests here. +from django.urls import reverse +from rest_framework.test import APITestCase + +from djangoapps.tests_data_setup import generate_classes + +from djangoapps.rooms.models import Room +from djangoapps.classes.models import Class + + +class RoomsTest(APITestCase): + + @classmethod + def setUpTestData(cls): + rooms = [ + Room( + id=0, + name='1', + university_building=1 + ), + Room( + id=1, + name='2', + university_building=1 + ), + Room( + id=2, + name='3', + university_building=2 + ), + Room( + id=3, + name='4', + university_building=3 + ), + ] + for room in rooms: + Room.save(room) + + classes = generate_classes(1, rooms=rooms) + for test_class in classes: + Class.save(test_class) + + def test_buildings(self): + """ + Testing BuildingsViewSet. + """ + url = reverse('rooms-buildings') + response = self.client.get(url, format='json') + json_response = json.loads(response.content) + + self.assertEqual(set(json_response), {1, 2, 3}) + + def test_rooms_in_building(self): + """ + Testing RoomsInBuildingList. + """ + url = reverse('rooms-in-building', kwargs={"building": 1}) + response = self.client.get(url, format='json') + json_response = json.loads(response.content) + + self.assertEqual(set(map(lambda v: v['id'], json_response)), {0, 1}) diff --git a/djangoapps/rooms/types.py b/djangoapps/rooms/types.py new file mode 100644 index 0000000..f54fac8 --- /dev/null +++ b/djangoapps/rooms/types.py @@ -0,0 +1,9 @@ +from djangoapps.rooms.models import Room +from typing import NamedTuple, List + + +class EmptyRooms(NamedTuple): + week_number: int + day_of_week: int + time_slot: int + rooms: List[Room] diff --git a/djangoapps/rooms/urls.py b/djangoapps/rooms/urls.py index de8bf3a..4d6e166 100644 --- a/djangoapps/rooms/urls.py +++ b/djangoapps/rooms/urls.py @@ -1,11 +1,10 @@ from rest_framework import routers from django.conf.urls import url, include -from rest_framework.urlpatterns import format_suffix_patterns -from djangoapps.rooms.views import RoomsViewSet, RoomsBuildingsViewSet, \ - RoomsInBuildingList +from djangoapps.rooms.views import RoomsViewSet, BuildingsViewSet, \ + RoomsInBuildingList, EmptyRoomsInBuildingList -buildings_list = RoomsBuildingsViewSet.as_view({ +buildings_list = BuildingsViewSet.as_view({ "get": "list", }) @@ -18,5 +17,8 @@ url(r'^rooms/building/(?P\d+)$', RoomsInBuildingList.as_view(), name="rooms-in-building"), + url(r'^rooms/free/building/(?P\d+)$', + EmptyRoomsInBuildingList.as_view(), name="empty-rooms-in-building"), + url(r'', include(router.urls)), ] diff --git a/djangoapps/rooms/views.py b/djangoapps/rooms/views.py index 09aa826..8367f93 100644 --- a/djangoapps/rooms/views.py +++ b/djangoapps/rooms/views.py @@ -4,8 +4,12 @@ from rest_framework.exceptions import ParseError from rest_framework.viewsets import ReadOnlyModelViewSet +from djangoapps.rooms.services import FreeRoomsInBuildingService + from djangoapps.rooms.models import Room -from djangoapps.rooms.serializers import RoomSerializer, RoomBuildingOnlySerializer +from djangoapps.classes.models import Class +from djangoapps.rooms.serializers import RoomSerializer, \ + RoomBuildingOnlySerializer, EmptyRoomsSerializer class RoomsViewSet(ReadOnlyModelViewSet): @@ -17,12 +21,12 @@ class RoomsViewSet(ReadOnlyModelViewSet): serializer_class = RoomSerializer -class RoomsBuildingsViewSet(ReadOnlyModelViewSet): +class BuildingsViewSet(ReadOnlyModelViewSet): """ A simple ViewSet for viewing of all buildings. """ - queryset = Room.objects.distinct('university_building') + queryset = Room.objects.values('university_building').distinct().all() serializer_class = RoomBuildingOnlySerializer @method_decorator(cache_page(10 * 60)) @@ -43,3 +47,16 @@ def get_queryset(self): return Room.objects.filter(university_building=building) except ValueError: raise ParseError + + +class EmptyRoomsInBuildingList(generics.ListAPIView): + serializer_class = EmptyRoomsSerializer + + def get_queryset(self): + try: + building = int(self.kwargs['building']) + all_rooms = list(Room.objects.filter(university_building=building)) + all_classes = list(Class.objects.filter(room__university_building=building)) + return FreeRoomsInBuildingService.get_all_empty_rooms(all_classes, all_rooms) + except ValueError: + raise ParseError diff --git a/djangoapps/teachers/migrations/0001_initial.py b/djangoapps/teachers/migrations/0001_initial.py new file mode 100644 index 0000000..ac37a1b --- /dev/null +++ b/djangoapps/teachers/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 3.0.7 on 2020-07-20 11:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Teacher', + fields=[ + ('id', models.IntegerField(primary_key=True, serialize=False)), + ('name', models.CharField(max_length=50)), + ('official_name', models.CharField(max_length=50)), + ], + options={ + 'db_table': 'teacher', + 'ordering': ('name',), + }, + ), + ] diff --git a/djangoapps/teachers/urls.py b/djangoapps/teachers/urls.py index f13ee8f..93e827d 100644 --- a/djangoapps/teachers/urls.py +++ b/djangoapps/teachers/urls.py @@ -1,8 +1,15 @@ +from django.conf.urls import url +from django.urls import include from rest_framework import routers -from djangoapps.teachers.views import TeachersViewSet +from djangoapps.teachers.views import TeachersViewSet, TeachersSearchByNameList router = routers.DefaultRouter() router.register(r'teachers', TeachersViewSet, basename='teachers') -urlpatterns = router.urls +urlpatterns = [ + url(r'^teachers/search/(?P.+)$', + TeachersSearchByNameList.as_view(), name="groups-search"), + + url(r'', include(router.urls)), +] diff --git a/djangoapps/teachers/views.py b/djangoapps/teachers/views.py index 8fdcf12..6e5c18a 100644 --- a/djangoapps/teachers/views.py +++ b/djangoapps/teachers/views.py @@ -1,3 +1,5 @@ +from django.db.models import Q +from rest_framework import generics from rest_framework.viewsets import ReadOnlyModelViewSet from djangoapps.teachers.models import Teacher @@ -11,3 +13,12 @@ class TeachersViewSet(ReadOnlyModelViewSet): queryset = Teacher.objects.all() serializer_class = TeacherSerializer + + +class TeachersSearchByNameList(generics.ListAPIView): + serializer_class = TeacherSerializer + + def get_queryset(self): + query: str = self.kwargs["query"] + return Teacher.objects.filter(Q(name__icontains=query) | + Q(official_name__icontains=query))[:10] diff --git a/djangoapps/tests_data_setup.py b/djangoapps/tests_data_setup.py new file mode 100644 index 0000000..75bf479 --- /dev/null +++ b/djangoapps/tests_data_setup.py @@ -0,0 +1,135 @@ +from typing import List, Union +import random + +from djangoapps.groups.models import Group +from djangoapps.classes.models import Class +from djangoapps.rooms.models import Room +from djangoapps.teachers.models import Teacher +from djangoapps.timeslots.models import TimeSlot + + +group_prefixes = ('км', 'кп', 'кв') + + +def generate_groups(num: int) -> List[Group]: + if num <= 0: + raise ValueError('num should be positive and greater then 0') + + return list(map( + lambda i: Group(id=i, name=f"{random.choice(group_prefixes)}-{i}"), + range(num) + )) + + +first_names = ('John', 'Andy', 'Joe', 'Oliver', 'Mason') +last_names = ('Johnson', 'Smith', 'Williams', 'Miller', 'Davis') + + +def generate_teachers(num: int) -> List[Teacher]: + if num <= 0: + raise ValueError('num should be positive and greater then 0') + + return list(map( + lambda i: Teacher( + id=i, + name=f"{random.choice(first_names)} {random.choice(last_names)}", + official_name=f"of. {random.choice(first_names)} {random.choice(last_names)}", + ), + range(num) + )) + + +def generate_rooms(num: int) -> List[Room]: + if num <= 0: + raise ValueError('num should be positive and greater then 0') + + return list(map( + lambda i: Room( + id=i, + name=str(i), + university_building=random.randint(0, 30), + ), + range(num) + )) + + +def generate_timeslots() -> List[TimeSlot]: + return [ + TimeSlot( + id=1, + time_start='08:30:00', + time_end='10:05:00' + ), + TimeSlot( + id=2, + time_start='10:25:00', + time_end='12:00:00' + ), + TimeSlot( + id=3, + time_start='12:20:00', + time_end='13:55:00' + ), + TimeSlot( + id=4, + time_start='14:15:00', + time_end='15:50:00' + ), + TimeSlot( + id=5, + time_start='16:10:00', + time_end='17:45:00' + ) + ] + + +classes_types = ('Лек', 'Лаб', 'Прак') + + +def generate_classes(num: int, + groups: Union[List[Group], None] = None, + rooms: Union[List[Room], None] = None, + teachers: Union[List[Teacher], None] = None, + time_slots: Union[List[TimeSlot], None] = None) -> List[Class]: + if num <= 0: + raise ValueError('num should be positive and greater then 0') + + if not groups: + groups = generate_groups(num) + for group in groups: + Group.save(group) + + if not rooms: + rooms = generate_rooms(num) + for room in rooms: + Room.save(room) + + if not teachers: + teachers = generate_teachers(num) + for teacher in teachers: + Teacher.save(teacher) + + if not time_slots: + time_slots = generate_timeslots() + for time_slot in time_slots: + TimeSlot.save(time_slot) + + res = list() + for i in range(num): + uni_class = Class( + id=i, + name=f"class {i}", + type=random.choice(classes_types), + + day_of_week=random.randint(1, 5), + week_number=random.randint(1, 2), + + room=rooms[i] if len(rooms) > i else random.choice(rooms), + teacher=teachers[i] if len(teachers) > i else random.choice(teachers), + time_slot=random.choice(time_slots) + ) + + uni_class.groups.add(groups[i] if len(groups) > i else random.choice(groups)) + res.append(uni_class) + + return res diff --git a/djangoapps/timeslots/migrations/0001_initial.py b/djangoapps/timeslots/migrations/0001_initial.py new file mode 100644 index 0000000..ae33722 --- /dev/null +++ b/djangoapps/timeslots/migrations/0001_initial.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.7 on 2020-07-20 11:44 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='TimeSlot', + fields=[ + ('id', models.IntegerField(primary_key=True, serialize=False)), + ('time_start', models.TimeField()), + ('time_end', models.TimeField()), + ], + options={ + 'db_table': 'time_slot', + }, + ), + ]