diff --git a/colloscope/models.py b/colloscope/models.py index 08925c2..dfa58b2 100644 --- a/colloscope/models.py +++ b/colloscope/models.py @@ -148,7 +148,8 @@ class Term(models.Model): .annotate(base_vol=Count("groups__members")) .annotate(swap_plus=Count("swap", filter=Q(swap__enroll=1), distinct=True)) .annotate(swap_minus=Count("swap", filter=Q(swap__enroll=0), distinct=True)) - .annotate(volume=F("base_vol") + F("swap_plus") - F("swap_minus"))) + .annotate(volume=F("base_vol") + F("swap_plus") - F("swap_minus")) + .order_by()) def query_colles_of_student(self, student) -> QuerySet: has_student = ((Q(groups__student=student) @@ -164,6 +165,7 @@ class Term(models.Model): .annotate(swap_minus=Count("pk", filter=Q(swap__enroll=0), distinct=True)) .annotate(volume=F("base_vol") + F("swap_plus") - F("swap_minus")) .filter(has_student) + .order_by() ) def query_colles_not_full_excluding_student(self, student) -> QuerySet: @@ -184,6 +186,9 @@ class Subject(models.Model): description = models.CharField(max_length=100) code = models.CharField(max_length=20) + class Meta: + ordering = ["description"] + def __str__(self): return self.description @@ -292,6 +297,7 @@ class Slot(models.Model): capacity = models.IntegerField() class Meta: + ordering = ["subject", "colleur", "day", "time"] verbose_name_plural = "slots" def __str__(self): diff --git a/colloscope/serializers.py b/colloscope/serializers.py new file mode 100644 index 0000000..57dbf64 --- /dev/null +++ b/colloscope/serializers.py @@ -0,0 +1,73 @@ +from rest_framework.serializers import ModelSerializer + +from colloscope.models import * + + +class SchoolSerializer(ModelSerializer): + class Meta: + model = School + fields = ["id", "uai", "description", "vacation"] + + +class StudentSerializer(ModelSerializer): + class Meta: + model = Student + fields = ["id", "cls", "first_name", "last_name", "groups"] + + +class ClassSerializer(ModelSerializer): + students = StudentSerializer(source="student_set", many=True) + + class Meta: + model = Class + fields = ["id", "school", "description", "year", "day_zero", "students"] + + +class TermSerializer(ModelSerializer): + class Meta: + model = Term + fields = ["id", "cls", "description", "begin", "end"] + + +class SubjectSerializer(ModelSerializer): + class Meta: + model = Subject + fields = ["id", "cls", "description", "code"] + + +class GroupTypeSerializer(ModelSerializer): + class Meta: + model = GroupType + fields = ["id", "term", "description"] + + +class GroupSerializer(ModelSerializer): + class Meta: + model = Group + fields = ["id", "term", "description", "members"] + + + + +class ColleurSerializer(ModelSerializer): + class Meta: + model = Colleur + fields = ["id", "gender", "name"] + + +class SlotSerializer(ModelSerializer): + class Meta: + model = Slot + fields = ["id", "term", "day", "time", "duration", "room", "subject", "colleur", "type", "capacity"] + + +class ColleSerializer(ModelSerializer): + class Meta: + model = Colle + fields = ["id", "slot", "groups", "date"] + + +class CalendarLinkSerializer(ModelSerializer): + class Meta: + model = CalendarLink + fields = ["id", "key", "student", "term"] \ No newline at end of file diff --git a/colloscope/viewsets.py b/colloscope/viewsets.py new file mode 100644 index 0000000..bfd0a1d --- /dev/null +++ b/colloscope/viewsets.py @@ -0,0 +1,82 @@ +from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet + +from colloscope.models import * +from colloscope.serializers import * + + +class SchoolViewset(ReadOnlyModelViewSet): + + serializer_class = SchoolSerializer + + def get_queryset(self): + return School.objects.all() + + +class ClassViewset(ReadOnlyModelViewSet): + serializer_class = ClassSerializer + + def get_queryset(self): + return Class.objects.all() + + +class TermViewset(ReadOnlyModelViewSet): + serializer_class = TermSerializer + + def get_queryset(self): + return Term.objects.all() + + +class SubjectViewset(ReadOnlyModelViewSet): + serializer_class = SubjectSerializer + + def get_queryset(self): + return Subject.objects.all() + + +class GroupTypeViewset(ReadOnlyModelViewSet): + serializer_class = GroupTypeSerializer + + def get_queryset(self): + return GroupType.objects.all() + + +class GroupViewset(ReadOnlyModelViewSet): + serializer_class = GroupSerializer + + def get_queryset(self): + return Group.objects.all() + + +class StudentViewset(ReadOnlyModelViewSet): + serializer_class = StudentSerializer + + def get_queryset(self): + return Student.objects.all() + + +class ColleurViewset(ReadOnlyModelViewSet): + serializer_class = ColleurSerializer + + def get_queryset(self): + return Colleur.objects.all() + + +class SlotViewset(ReadOnlyModelViewSet): + serializer_class = SlotSerializer + + def get_queryset(self): + return Slot.objects.all() + + +class ColleViewset(ReadOnlyModelViewSet): + serializer_class = ColleSerializer + + def get_queryset(self): + return Colle.objects.all() + + +class CalendarLinkViewset(ReadOnlyModelViewSet): + serializer_class = CalendarLinkSerializer + + def get_queryset(self): + return CalendarLink.objects.all() diff --git a/kholles_web/settings.py b/kholles_web/settings.py index 33603d5..f1400a2 100644 --- a/kholles_web/settings.py +++ b/kholles_web/settings.py @@ -42,16 +42,21 @@ CORS_ORIGIN_WHITELIST = [ INSTALLED_APPS = [ "daphne", 'django.contrib.admin', + 'oauth2_provider', + 'corsheaders', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + "rest_framework", 'colloscope', + "drf_spectacular", ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.middleware.common.CommonMiddleware', @@ -163,4 +168,20 @@ EMAIL_HOST = "pulp.o2switch.net" EMAIL_PORT = 587 EMAIL_HOST_USER = "colloscope@mp2i-vms.fr" EMAIL_HOST_PASSWORD = "phoBTchc2vVaq$PefsntT9M7" -EMAIL_USE_TLS = True \ No newline at end of file +EMAIL_USE_TLS = True + + +REST_FRAMEWORK = { + "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", + 'PAGE_SIZE': 10, + 'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema', +} + + +SPECTACULAR_SETTINGS = { + 'TITLE': 'Colloscope API', + 'DESCRIPTION': 'Gérer les colles de prépa devient facile', + 'VERSION': '1.0.0', + 'SERVE_INCLUDE_SCHEMA': False, + # OTHER SETTINGS +} diff --git a/kholles_web/urls.py b/kholles_web/urls.py index afbc15e..23cf0aa 100644 --- a/kholles_web/urls.py +++ b/kholles_web/urls.py @@ -17,11 +17,35 @@ Including another URLconf from django.contrib import admin, auth from django.urls import include, path from django.contrib.staticfiles import views as vstatic +from rest_framework import routers +from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView from colloscope.views import home_redirect +from colloscope.viewsets import * + +router = routers.SimpleRouter() +router.register('school', SchoolViewset, basename='school') +router.register('class', ClassViewset, basename='class') +router.register('term', TermViewset, basename='term') +router.register("subject", SubjectViewset, basename='subject') +router.register('grouptype', GroupTypeViewset, basename='grouptype') +router.register("group", GroupViewset, basename='group') +router.register("student", StudentViewset, basename='student') +router.register("colleur", ColleurViewset, basename='colleur') +router.register("slot", SlotViewset, basename='slot') +router.register("colle", ColleViewset, basename='colle') +router.register("calendarlink", CalendarLinkViewset, basename='calendarlink') + urlpatterns = [ path('', home_redirect, name="home"), + path('api-auth/', include('rest_framework.urls')), + path("api/", include(router.urls)), + path('api/schema/', SpectacularAPIView.as_view(), name='schema'), + path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), + path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'), + + path("oauth2/", include('oauth2_provider.urls', namespace='oauth2_provider')), path("favicon.ico", lambda req: vstatic.serve(req, "favicon.ico")), path('colloscope/', include('colloscope.urls')), path('admin/', admin.site.urls), diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..43e7b32 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,58 @@ +aiohttp==3.9.5 +aiosignal==1.3.1 +asgiref==3.8.1 +attrs==23.2.0 +autobahn==23.6.2 +Automat==22.10.0 +certifi==2024.2.2 +cffi==1.16.0 +charset-normalizer==3.3.2 +constantly==23.10.4 +cryptography==42.0.5 +daphne==4.1.2 +defusedxml==0.7.1 +discord.py==2.3.2 +Django==5.0.4 +django-cors-headers==4.3.1 +django-oauth-toolkit==2.3.0 +django-smtp-ssl==1.0 +djangorestframework==3.15.1 +drf-spectacular==0.27.2 +fonttools==4.51.0 +fpdf2==2.7.8 +frozenlist==1.4.1 +hyperlink==21.0.0 +icalendar==5.0.12 +idna==3.7 +incremental==22.10.0 +inflection==0.5.1 +jsonschema==4.22.0 +jsonschema-specifications==2023.12.1 +jwcrypto==1.5.6 +multidict==6.0.5 +numpy==1.26.4 +oauthlib==3.2.2 +pandas==2.2.2 +pillow==10.3.0 +pyasn1==0.6.0 +pyasn1_modules==0.4.0 +pycparser==2.22 +pyOpenSSL==24.1.0 +python-dateutil==2.9.0.post0 +pytz==2024.1 +PyYAML==6.0.1 +referencing==0.35.1 +requests==2.31.0 +rpds-py==0.18.0 +service-identity==24.1.0 +setuptools==69.5.1 +six==1.16.0 +sqlparse==0.4.4 +Twisted==24.3.0 +txaio==23.1.1 +typing_extensions==4.11.0 +tzdata==2024.1 +uritemplate==4.1.1 +urllib3==2.2.1 +yarl==1.9.4 +zope.interface==6.3