From 11154d2fd1efa46f90d5ec8bbaf60049377ba954 Mon Sep 17 00:00:00 2001 From: papi Date: Tue, 26 Dec 2023 00:25:09 +0300 Subject: [PATCH] Resolve lint warnings --- accounts/admin.py | 26 +- accounts/api/serializers.py | 8 +- accounts/api/urls.py | 7 +- accounts/api/views.py | 13 +- accounts/apps.py | 2 +- accounts/decorators.py | 18 +- accounts/forms.py | 353 +++++++++++++++++--------- accounts/models.py | 43 ++-- accounts/urls.py | 80 +++--- accounts/validators.py | 6 +- app/apps.py | 2 +- app/forms.py | 42 +-- app/models.py | 18 +- app/urls.py | 48 ++-- app/views.py | 180 +++++++------ course/apps.py | 2 +- course/decorators.py | 2 +- course/forms.py | 71 +++--- course/models.py | 122 ++++++--- course/urls.py | 86 ++++--- course/utils.py | 11 +- course/views.py | 361 ++++++++++++++++---------- payments/apps.py | 2 +- payments/models.py | 10 +- payments/urls.py | 25 +- payments/views.py | 174 +++++++------ quiz/admin.py | 55 ++-- quiz/apps.py | 2 +- quiz/forms.py | 27 +- quiz/models.py | 297 +++++++++++++++------- quiz/urls.py | 32 +-- quiz/utils.py | 11 +- quiz/views.py | 206 +++++++++------ result/admin.py | 12 +- result/apps.py | 2 +- result/models.py | 80 +++--- result/urls.py | 24 +- result/views.py | 494 ++++++++++++++++++++++++------------ search/apps.py | 2 +- search/urls.py | 2 +- search/views.py | 47 ++-- 41 files changed, 1878 insertions(+), 1127 deletions(-) diff --git a/accounts/admin.py b/accounts/admin.py index 0a51a87..8c94543 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -5,13 +5,31 @@ from .models import User, Student, Parent class UserAdmin(admin.ModelAdmin): - list_display = ['get_full_name', 'username', 'email', 'is_active', 'is_student', 'is_lecturer', 'is_parent', 'is_staff'] - search_fields = ['username', 'first_name', 'last_name', 'email', 'is_active', 'is_lecturer', 'is_parent', 'is_staff'] + list_display = [ + "get_full_name", + "username", + "email", + "is_active", + "is_student", + "is_lecturer", + "is_parent", + "is_staff", + ] + search_fields = [ + "username", + "first_name", + "last_name", + "email", + "is_active", + "is_lecturer", + "is_parent", + "is_staff", + ] class Meta: managed = True - verbose_name = 'User' - verbose_name_plural = 'Users' + verbose_name = "User" + verbose_name_plural = "Users" # class ScoreAdmin(admin.ModelAdmin): diff --git a/accounts/api/serializers.py b/accounts/api/serializers.py index 8f40b38..5a8cb2b 100644 --- a/accounts/api/serializers.py +++ b/accounts/api/serializers.py @@ -1,10 +1,8 @@ from rest_framework import serializers -from django.contrib.auth.views import get_user_model - -User = get_user_model() +from django.contrib.auth import get_user_model class UserSerializer(serializers.ModelSerializer): class Meta: - model = User - fields = '__all__' + model = get_user_model() + fields = "__all__" diff --git a/accounts/api/urls.py b/accounts/api/urls.py index fc6a8b0..97f91be 100644 --- a/accounts/api/urls.py +++ b/accounts/api/urls.py @@ -1,9 +1,8 @@ -from . import views - from django.urls import path +from . import views app_name = "accounts-api" urlpatterns = [ - path('', views.UserListAPIView.as_view(), name="users-api"), -] \ No newline at end of file + path("", views.UserListAPIView.as_view(), name="users-api"), +] diff --git a/accounts/api/views.py b/accounts/api/views.py index 958d0f5..4bc8365 100644 --- a/accounts/api/views.py +++ b/accounts/api/views.py @@ -1,24 +1,23 @@ from rest_framework import generics -from django.contrib.auth.views import get_user_model +from django.contrib.auth import get_user_model from .serializers import UserSerializer -User = get_user_model() - class UserListAPIView(generics.ListAPIView): - lookup_field = 'id' + lookup_field = "id" serializer_class = UserSerializer def get_queryset(self): - qs = User.objects.all() - q = self.request.GET.get('q') + qs = get_user_model().objects.all() + q = self.request.GET.get("q") if q is not None: qs = qs.filter(username__iexact=q) return qs class UserDetailView(generics.RetrieveAPIView): - lookup_field = 'id' + User = get_user_model() + lookup_field = "id" queryset = User.objects.all() model = User diff --git a/accounts/apps.py b/accounts/apps.py index 9b3fc5a..fb0257e 100644 --- a/accounts/apps.py +++ b/accounts/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class AccountsConfig(AppConfig): - name = 'accounts' + name = "accounts" diff --git a/accounts/decorators.py b/accounts/decorators.py index 5672166..162aa1b 100644 --- a/accounts/decorators.py +++ b/accounts/decorators.py @@ -3,7 +3,9 @@ from django.http import Http404 from django.contrib.auth.decorators import user_passes_test -def student_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=Http404): +def student_required( + function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=Http404 +): """ Decorator for views that checks that the logged in user is a student, redirects to the log-in page if necessary. @@ -11,14 +13,16 @@ def student_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, log actual_decorator = user_passes_test( lambda u: u.is_active and u.is_student or u.is_superuser, login_url=login_url, - redirect_field_name=redirect_field_name + redirect_field_name=redirect_field_name, ) if function: return actual_decorator(function) return actual_decorator -def lecturer_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=Http404): +def lecturer_required( + function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=Http404 +): """ Decorator for views that checks that the logged in user is a teacher, redirects to the log-in page if necessary. @@ -26,14 +30,16 @@ def lecturer_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, lo actual_decorator = user_passes_test( lambda u: u.is_active and u.is_lecturer or u.is_superuser, login_url=login_url, - redirect_field_name=redirect_field_name + redirect_field_name=redirect_field_name, ) if function: return actual_decorator(function) return actual_decorator -def admin_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=Http404): +def admin_required( + function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=Http404 +): """ Decorator for views that checks that the logged in user is a teacher, redirects to the log-in page if necessary. @@ -41,7 +47,7 @@ def admin_required(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login actual_decorator = user_passes_test( lambda u: u.is_active and u.is_superuser, login_url=login_url, - redirect_field_name=redirect_field_name + redirect_field_name=redirect_field_name, ) if function: return actual_decorator(function) diff --git a/accounts/forms.py b/accounts/forms.py index 2402080..da3b546 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -1,82 +1,59 @@ from django import forms from django.db import transaction -from django.contrib.auth.forms import UserCreationForm, UserChangeForm, PasswordChangeForm +from django.contrib.auth.forms import ( + UserCreationForm, + UserChangeForm, +) + # from django.contrib.auth.models import User from django.contrib.auth.forms import PasswordResetForm from course.models import Program + # from .models import User, Student, LEVEL from .models import * class StaffAddForm(UserCreationForm): - username = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="Username", ) - - first_name = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="First Name", ) - - last_name = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="Last Name", ) - - address = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="Address", ) - - phone = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="Mobile No.", ) - - email = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="Email", ) - - password1 = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'password', 'class': 'form-control', }), - label="Password", ) - - password2 = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'password', 'class': 'form-control', }), - label="Password Confirmation", ) - - class Meta(UserCreationForm.Meta): - model = User - - @transaction.atomic() - def save(self, commit=True): - user = super().save(commit=False) - user.is_lecturer = True - user.first_name = self.cleaned_data.get('first_name') - user.last_name = self.cleaned_data.get('last_name') - user.phone = self.cleaned_data.get('phone') - user.address = self.cleaned_data.get('address') - user.email = self.cleaned_data.get('email') - if commit: - user.save() - return user - - -class StudentAddForm(UserCreationForm): username = forms.CharField( max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', - 'id': 'username_id' + "type": "text", + "class": "form-control", } ), label="Username", ) + + first_name = forms.CharField( + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", + } + ), + label="First Name", + ) + + last_name = forms.CharField( + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", + } + ), + label="Last Name", + ) + address = forms.CharField( max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", } ), label="Address", @@ -86,8 +63,88 @@ class StudentAddForm(UserCreationForm): max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", + } + ), + label="Mobile No.", + ) + + email = forms.CharField( + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", + } + ), + label="Email", + ) + + password1 = forms.CharField( + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "password", + "class": "form-control", + } + ), + label="Password", + ) + + password2 = forms.CharField( + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "password", + "class": "form-control", + } + ), + label="Password Confirmation", + ) + + class Meta(UserCreationForm.Meta): + model = User + + @transaction.atomic() + def save(self, commit=True): + user = super().save(commit=False) + user.is_lecturer = True + user.first_name = self.cleaned_data.get("first_name") + user.last_name = self.cleaned_data.get("last_name") + user.phone = self.cleaned_data.get("phone") + user.address = self.cleaned_data.get("address") + user.email = self.cleaned_data.get("email") + if commit: + user.save() + return user + + +class StudentAddForm(UserCreationForm): + username = forms.CharField( + max_length=30, + widget=forms.TextInput( + attrs={"type": "text", "class": "form-control", "id": "username_id"} + ), + label="Username", + ) + address = forms.CharField( + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", + } + ), + label="Address", + ) + + phone = forms.CharField( + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", } ), label="Mobile No.", @@ -97,8 +154,8 @@ class StudentAddForm(UserCreationForm): max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", } ), label="First name", @@ -108,8 +165,8 @@ class StudentAddForm(UserCreationForm): max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", } ), label="Last name", @@ -119,34 +176,50 @@ class StudentAddForm(UserCreationForm): widget=forms.Select( choices=LEVEL, attrs={ - 'class': 'browser-default custom-select form-control', - } + "class": "browser-default custom-select form-control", + }, ), ) department = forms.ModelChoiceField( queryset=Program.objects.all(), - widget=forms.Select(attrs={'class': 'browser-default custom-select form-control'}), + widget=forms.Select( + attrs={"class": "browser-default custom-select form-control"} + ), label="Department", ) email = forms.EmailField( widget=forms.TextInput( attrs={ - 'type': 'email', - 'class': 'form-control', + "type": "email", + "class": "form-control", } ), label="Email Address", ) password1 = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'password', 'class': 'form-control', }), - label="Password", ) + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "password", + "class": "form-control", + } + ), + label="Password", + ) password2 = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'password', 'class': 'form-control', }), - label="Password Confirmation", ) + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "password", + "class": "form-control", + } + ), + label="Password Confirmation", + ) # def validate_email(self): # email = self.cleaned_data['email'] @@ -160,16 +233,16 @@ class StudentAddForm(UserCreationForm): def save(self): user = super().save(commit=False) user.is_student = True - user.first_name = self.cleaned_data.get('first_name') - user.last_name = self.cleaned_data.get('last_name') - user.address = self.cleaned_data.get('address') - user.phone = self.cleaned_data.get('phone') - user.email = self.cleaned_data.get('email') + user.first_name = self.cleaned_data.get("first_name") + user.last_name = self.cleaned_data.get("last_name") + user.address = self.cleaned_data.get("address") + user.phone = self.cleaned_data.get("phone") + user.email = self.cleaned_data.get("email") user.save() student = Student.objects.create( student=user, - level=self.cleaned_data.get('level'), - department=self.cleaned_data.get('department') + level=self.cleaned_data.get("level"), + department=self.cleaned_data.get("department"), ) student.save() return user @@ -177,36 +250,66 @@ class StudentAddForm(UserCreationForm): class ProfileUpdateForm(UserChangeForm): email = forms.EmailField( - widget=forms.TextInput(attrs={'type': 'email', 'class': 'form-control', }), - label="Email Address", ) + widget=forms.TextInput( + attrs={ + "type": "email", + "class": "form-control", + } + ), + label="Email Address", + ) first_name = forms.CharField( - widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="First Name", ) + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", + } + ), + label="First Name", + ) last_name = forms.CharField( - widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="Last Name", ) + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", + } + ), + label="Last Name", + ) phone = forms.CharField( - widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="Phone No.", ) + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", + } + ), + label="Phone No.", + ) address = forms.CharField( - widget=forms.TextInput(attrs={'type': 'text', 'class': 'form-control', }), - label="Address / city", ) + widget=forms.TextInput( + attrs={ + "type": "text", + "class": "form-control", + } + ), + label="Address / city", + ) class Meta: model = User - fields = ['email', 'phone', 'address', 'picture', 'first_name', 'last_name'] + fields = ["email", "phone", "address", "picture", "first_name", "last_name"] class EmailValidationOnForgotPassword(PasswordResetForm): def clean_email(self): - email = self.cleaned_data['email'] + email = self.cleaned_data["email"] if not User.objects.filter(email__iexact=email, is_active=True).exists(): msg = "There is no user registered with the specified E-mail address. " - self.add_error('email', msg) + self.add_error("email", msg) return email @@ -215,8 +318,8 @@ class ParentAddForm(UserCreationForm): max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", } ), label="Username", @@ -225,8 +328,8 @@ class ParentAddForm(UserCreationForm): max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", } ), label="Address", @@ -236,8 +339,8 @@ class ParentAddForm(UserCreationForm): max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", } ), label="Mobile No.", @@ -247,8 +350,8 @@ class ParentAddForm(UserCreationForm): max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", } ), label="First name", @@ -258,8 +361,8 @@ class ParentAddForm(UserCreationForm): max_length=30, widget=forms.TextInput( attrs={ - 'type': 'text', - 'class': 'form-control', + "type": "text", + "class": "form-control", } ), label="Last name", @@ -268,8 +371,8 @@ class ParentAddForm(UserCreationForm): email = forms.EmailField( widget=forms.TextInput( attrs={ - 'type': 'email', - 'class': 'form-control', + "type": "email", + "class": "form-control", } ), label="Email Address", @@ -277,24 +380,42 @@ class ParentAddForm(UserCreationForm): student = forms.ModelChoiceField( queryset=Student.objects.all(), - widget=forms.Select(attrs={'class': 'browser-default custom-select form-control'}), + widget=forms.Select( + attrs={"class": "browser-default custom-select form-control"} + ), label="Student", ) relation_ship = forms.CharField( widget=forms.Select( choices=RELATION_SHIP, - attrs={'class': 'browser-default custom-select form-control',} + attrs={ + "class": "browser-default custom-select form-control", + }, ), ) password1 = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'password', 'class': 'form-control', }), - label="Password", ) + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "password", + "class": "form-control", + } + ), + label="Password", + ) password2 = forms.CharField( - max_length=30, widget=forms.TextInput(attrs={'type': 'password', 'class': 'form-control', }), - label="Password Confirmation", ) + max_length=30, + widget=forms.TextInput( + attrs={ + "type": "password", + "class": "form-control", + } + ), + label="Password Confirmation", + ) # def validate_email(self): # email = self.cleaned_data['email'] @@ -308,16 +429,16 @@ class ParentAddForm(UserCreationForm): def save(self): user = super().save(commit=False) user.is_parent = True - user.first_name = self.cleaned_data.get('first_name') - user.last_name = self.cleaned_data.get('last_name') - user.address = self.cleaned_data.get('address') - user.phone = self.cleaned_data.get('phone') - user.email = self.cleaned_data.get('email') + user.first_name = self.cleaned_data.get("first_name") + user.last_name = self.cleaned_data.get("last_name") + user.address = self.cleaned_data.get("address") + user.phone = self.cleaned_data.get("phone") + user.email = self.cleaned_data.get("email") user.save() parent = Parent.objects.create( user=user, - student=self.cleaned_data.get('student'), - relation_ship=self.cleaned_data.get('relation_ship') + student=self.cleaned_data.get("student"), + relation_ship=self.cleaned_data.get("relation_ship"), ) parent.save() return user diff --git a/accounts/models.py b/accounts/models.py index f9bc7cc..9fc0724 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -28,7 +28,7 @@ GRAND_MOTHER = "Grand mother" GRAND_FATHER = "Grand father" OTHER = "Other" -RELATION_SHIP = ( +RELATION_SHIP = ( (FATHER, "Father"), (MOTHER, "Mother"), (BROTHER, "Brother"), @@ -38,16 +38,20 @@ RELATION_SHIP = ( (OTHER, "Other"), ) + class UserManager(UserManager): def search(self, query=None): qs = self.get_queryset() if query is not None: - or_lookup = (Q(username__icontains=query) | - Q(first_name__icontains=query)| - Q(last_name__icontains=query)| - Q(email__icontains=query) - ) - qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups + or_lookup = ( + Q(username__icontains=query) + | Q(first_name__icontains=query) + | Q(last_name__icontains=query) + | Q(email__icontains=query) + ) + qs = qs.filter( + or_lookup + ).distinct() # distinct() is often necessary with Q lookups return qs @@ -58,7 +62,9 @@ class User(AbstractUser): is_dep_head = models.BooleanField(default=False) phone = models.CharField(max_length=60, blank=True, null=True) address = models.CharField(max_length=60, blank=True, null=True) - picture = models.ImageField(upload_to='profile_pictures/%y/%m/%d/', default='default.png', null=True) + picture = models.ImageField( + upload_to="profile_pictures/%y/%m/%d/", default="default.png", null=True + ) email = models.EmailField(blank=True, null=True) username_validator = ASCIIUsernameValidator() @@ -73,7 +79,7 @@ class User(AbstractUser): return full_name def __str__(self): - return '{} ({})'.format(self.username, self.get_full_name) + return "{} ({})".format(self.username, self.get_full_name) @property def get_user_role(self): @@ -90,11 +96,11 @@ class User(AbstractUser): try: return self.picture.url except: - no_picture = settings.MEDIA_URL + 'default.png' + no_picture = settings.MEDIA_URL + "default.png" return no_picture def get_absolute_url(self): - return reverse('profile_single', kwargs={'id': self.id}) + return reverse("profile_single", kwargs={"id": self.id}) def save(self, *args, **kwargs): super().save(*args, **kwargs) @@ -108,7 +114,7 @@ class User(AbstractUser): pass def delete(self, *args, **kwargs): - if self.picture.url != settings.MEDIA_URL + 'default.png': + if self.picture.url != settings.MEDIA_URL + "default.png": self.picture.delete() super().delete(*args, **kwargs) @@ -117,10 +123,10 @@ class StudentManager(models.Manager): def search(self, query=None): qs = self.get_queryset() if query is not None: - or_lookup = (Q(level__icontains=query) | - Q(department__icontains=query) - ) - qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups + or_lookup = Q(level__icontains=query) | Q(department__icontains=query) + qs = qs.filter( + or_lookup + ).distinct() # distinct() is often necessary with Q lookups return qs @@ -136,7 +142,7 @@ class Student(models.Model): return self.student.get_full_name def get_absolute_url(self): - return reverse('profile_single', kwargs={'id': self.id}) + return reverse("profile_single", kwargs={"id": self.id}) def delete(self, *args, **kwargs): self.student.delete() @@ -145,9 +151,10 @@ class Student(models.Model): class Parent(models.Model): """ - Connect student with their parent, parents can + Connect student with their parent, parents can only view their connected students information """ + user = models.OneToOneField(User, on_delete=models.CASCADE) student = models.OneToOneField(Student, null=True, on_delete=models.SET_NULL) first_name = models.CharField(max_length=120) diff --git a/accounts/urls.py b/accounts/urls.py index 4e2cee4..20bd1cd 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -1,57 +1,59 @@ from django.urls import path, include from django.contrib.auth.views import ( - PasswordResetView, PasswordResetDoneView, PasswordResetConfirmView, - PasswordResetCompleteView, LoginView, LogoutView - ) + PasswordResetView, + PasswordResetDoneView, + PasswordResetConfirmView, + PasswordResetCompleteView, + LoginView, + LogoutView, +) from .views import ( - profile, profile_single, admin_panel, - profile_update, change_password, - LecturerListView, StudentListView, - staff_add_view, edit_staff, - delete_staff, student_add_view, - edit_student, delete_student, ParentAdd, validate_username, register - ) + profile, + profile_single, + admin_panel, + profile_update, + change_password, + LecturerListView, + StudentListView, + staff_add_view, + edit_staff, + delete_staff, + student_add_view, + edit_student, + delete_student, + ParentAdd, + validate_username, + register, +) from .forms import EmailValidationOnForgotPassword urlpatterns = [ - path('', include('django.contrib.auth.urls')), - - path('admin_panel/', admin_panel, name='admin_panel'), - - path('profile/', profile, name='profile'), - path('profile//detail/', profile_single, name='profile_single'), - path('setting/', profile_update, name='edit_profile'), - path('change_password/', change_password, name='change_password'), - - path('lecturers/', LecturerListView.as_view(), name='lecturer_list'), - path('lecturer/add/', staff_add_view, name='add_lecturer'), - path('staff//edit/', edit_staff, name='staff_edit'), - path('lecturers//delete/', delete_staff, name='lecturer_delete'), - - path('students/', StudentListView.as_view(), name='student_list'), - path('student/add/', student_add_view, name='add_student'), - path('student//edit/', edit_student, name='student_edit'), - path('students//delete/', delete_student, name='student_delete'), - - path('parents/add/', ParentAdd.as_view(), name='add_parent'), - - path('ajax/validate-username/', validate_username, name='validate_username'), - - path('register/', register, name='register'), - + path("", include("django.contrib.auth.urls")), + path("admin_panel/", admin_panel, name="admin_panel"), + path("profile/", profile, name="profile"), + path("profile//detail/", profile_single, name="profile_single"), + path("setting/", profile_update, name="edit_profile"), + path("change_password/", change_password, name="change_password"), + path("lecturers/", LecturerListView.as_view(), name="lecturer_list"), + path("lecturer/add/", staff_add_view, name="add_lecturer"), + path("staff//edit/", edit_staff, name="staff_edit"), + path("lecturers//delete/", delete_staff, name="lecturer_delete"), + path("students/", StudentListView.as_view(), name="student_list"), + path("student/add/", student_add_view, name="add_student"), + path("student//edit/", edit_student, name="student_edit"), + path("students//delete/", delete_student, name="student_delete"), + path("parents/add/", ParentAdd.as_view(), name="add_parent"), + path("ajax/validate-username/", validate_username, name="validate_username"), + path("register/", register, name="register"), # path('add-student/', StudentAddView.as_view(), name='add_student'), - # path('programs/course/delete//', course_delete, name='delete_course'), - # Setting urls # path('profile//edit/', profileUpdateView, name='edit_profile'), # path('profile//change-password/', changePasswordView, name='change_password'), - # ################################################################ # path('login/', LoginView.as_view(), name='login'), # path('logout/', LogoutView.as_view(), name='logout', kwargs={'next_page': '/'}), - # path('password-reset/', PasswordResetView.as_view( # form_class=EmailValidationOnForgotPassword, # template_name='registration/password_reset.html' diff --git a/accounts/validators.py b/accounts/validators.py index 248c7c8..fb3a01e 100644 --- a/accounts/validators.py +++ b/accounts/validators.py @@ -7,9 +7,9 @@ from django.utils.translation import gettext_lazy as _ @deconstructible class ASCIIUsernameValidator(validators.RegexValidator): - regex = r'^[a-zA-Z]+\/(...)\/(....)' + regex = r"^[a-zA-Z]+\/(...)\/(....)" message = _( - 'Enter a valid username. This value may contain only English letters, ' - 'numbers, and @/./+/-/_ characters.' + "Enter a valid username. This value may contain only English letters, " + "numbers, and @/./+/-/_ characters." ) flags = re.ASCII diff --git a/app/apps.py b/app/apps.py index 80b2c8d..200c598 100644 --- a/app/apps.py +++ b/app/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class AppConfig(AppConfig): - name = 'app' + name = "app" diff --git a/app/forms.py b/app/forms.py index 38fb9a9..d30a557 100644 --- a/app/forms.py +++ b/app/forms.py @@ -8,27 +8,32 @@ from .models import NewsAndEvents, Session, Semester, SEMESTER class NewsAndEventsForm(forms.ModelForm): class Meta: model = NewsAndEvents - fields = ('title', 'summary', 'posted_as',) + fields = ( + "title", + "summary", + "posted_as", + ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['title'].widget.attrs.update({'class': 'form-control'}) - self.fields['summary'].widget.attrs.update({'class': 'form-control'}) - self.fields['posted_as'].widget.attrs.update({'class': 'form-control'}) + self.fields["title"].widget.attrs.update({"class": "form-control"}) + self.fields["summary"].widget.attrs.update({"class": "form-control"}) + self.fields["posted_as"].widget.attrs.update({"class": "form-control"}) class SessionForm(forms.ModelForm): next_session_begins = forms.DateTimeField( widget=forms.TextInput( attrs={ - 'type': 'date', + "type": "date", } ), - required=True) + required=True, + ) class Meta: model = Session - fields = ['session', 'is_current_session', 'next_session_begins'] + fields = ["session", "is_current_session", "next_session_begins"] class SemesterForm(forms.ModelForm): @@ -36,17 +41,17 @@ class SemesterForm(forms.ModelForm): widget=forms.Select( choices=SEMESTER, attrs={ - 'class': 'browser-default custom-select', - } + "class": "browser-default custom-select", + }, ), label="semester", ) is_current_semester = forms.CharField( widget=forms.Select( - choices=((True, 'Yes'), (False, 'No')), + choices=((True, "Yes"), (False, "No")), attrs={ - 'class': 'browser-default custom-select', - } + "class": "browser-default custom-select", + }, ), label="is current semester ?", ) @@ -54,21 +59,22 @@ class SemesterForm(forms.ModelForm): queryset=Session.objects.all(), widget=forms.Select( attrs={ - 'class': 'browser-default custom-select', + "class": "browser-default custom-select", } ), - required=True + required=True, ) next_semester_begins = forms.DateTimeField( widget=forms.TextInput( attrs={ - 'type': 'date', - 'class': 'form-control', + "type": "date", + "class": "form-control", } ), - required=True) + required=True, + ) class Meta: model = Semester - fields = ['semester', 'is_current_semester', 'session', 'next_semester_begins'] + fields = ["semester", "is_current_semester", "session", "next_semester_begins"] diff --git a/app/models.py b/app/models.py index cd4e16b..a13ac44 100644 --- a/app/models.py +++ b/app/models.py @@ -24,12 +24,12 @@ SEMESTER = ( class NewsAndEventsQuerySet(models.query.QuerySet): - def search(self, query): - lookups = (Q(title__icontains=query) | - Q(summary__icontains=query) | - Q(posted_as__icontains=query) - ) + lookups = ( + Q(title__icontains=query) + | Q(summary__icontains=query) + | Q(posted_as__icontains=query) + ) return self.filter(lookups).distinct() @@ -41,7 +41,9 @@ class NewsAndEventsManager(models.Manager): return self.get_queryset() def get_by_id(self, id): - qs = self.get_queryset().filter(id=id) # NewsAndEvents.objects == self.get_queryset() + qs = self.get_queryset().filter( + id=id + ) # NewsAndEvents.objects == self.get_queryset() if qs.count() == 1: return qs.first() return None @@ -75,7 +77,9 @@ class Session(models.Model): class Semester(models.Model): semester = models.CharField(max_length=10, choices=SEMESTER, blank=True) is_current_semester = models.BooleanField(default=False, blank=True, null=True) - session = models.ForeignKey(Session, on_delete=models.CASCADE, blank=True, null=True) + session = models.ForeignKey( + Session, on_delete=models.CASCADE, blank=True, null=True + ) next_semester_begins = models.DateField(null=True, blank=True) def __str__(self): diff --git a/app/urls.py b/app/urls.py index 1bce599..93bdf6b 100644 --- a/app/urls.py +++ b/app/urls.py @@ -1,29 +1,35 @@ from django.urls import path from .views import ( - home_view, post_add, edit_post, delete_post, - session_list_view, session_add_view, session_update_view, session_delete_view, - semester_list_view, semester_add_view, semester_update_view, semester_delete_view, - dashboard_view -) + home_view, + post_add, + edit_post, + delete_post, + session_list_view, + session_add_view, + session_update_view, + session_delete_view, + semester_list_view, + semester_add_view, + semester_update_view, + semester_delete_view, + dashboard_view, +) urlpatterns = [ # Accounts url - path('', home_view, name='home'), - path('add_item/', post_add, name='add_item'), - path('item//edit/', edit_post, name='edit_post'), - path('item//delete/', delete_post, name='delete_post'), - - path('session/', session_list_view, name="session_list"), - path('session/add/', session_add_view, name="add_session"), - path('session//edit/', session_update_view, name="edit_session"), - path('session//delete/', session_delete_view, name="delete_session"), - - path('semester/', semester_list_view, name="semester_list"), - path('semester/add/', semester_add_view, name="add_semester"), - path('semester//edit/', semester_update_view, name="edit_semester"), - path('semester//delete/', semester_delete_view, name="delete_semester"), - - path('dashboard/', dashboard_view, name="dashboard"), + path("", home_view, name="home"), + path("add_item/", post_add, name="add_item"), + path("item//edit/", edit_post, name="edit_post"), + path("item//delete/", delete_post, name="delete_post"), + path("session/", session_list_view, name="session_list"), + path("session/add/", session_add_view, name="add_session"), + path("session//edit/", session_update_view, name="edit_session"), + path("session//delete/", session_delete_view, name="delete_session"), + path("semester/", semester_list_view, name="semester_list"), + path("semester/add/", semester_add_view, name="add_semester"), + path("semester//edit/", semester_update_view, name="edit_semester"), + path("semester//delete/", semester_delete_view, name="delete_semester"), + path("dashboard/", dashboard_view, name="dashboard"), ] diff --git a/app/views.py b/app/views.py index 14ffce1..fa9f359 100644 --- a/app/views.py +++ b/app/views.py @@ -13,54 +13,62 @@ from .models import * # ######################################################## @login_required def home_view(request): - items = NewsAndEvents.objects.all().order_by('-updated_date') + items = NewsAndEvents.objects.all().order_by("-updated_date") context = { - 'title': "News & Events | DjangoSMS", - 'items': items, + "title": "News & Events | DjangoSMS", + "items": items, } - return render(request, 'app/index.html', context) + return render(request, "app/index.html", context) @login_required def post_add(request): - if request.method == 'POST': + if request.method == "POST": form = NewsAndEventsForm(request.POST) - title = request.POST.get('title') + title = request.POST.get("title") if form.is_valid(): form.save() - messages.success(request, (title + ' has been uploaded.')) - return redirect('home') + messages.success(request, (title + " has been uploaded.")) + return redirect("home") else: - messages.error(request, 'Please correct the error(s) below.') + messages.error(request, "Please correct the error(s) below.") else: form = NewsAndEventsForm() - return render(request, 'app/post_add.html', { - 'title': 'Add Post | DjangoSMS', - 'form': form, - }) + return render( + request, + "app/post_add.html", + { + "title": "Add Post | DjangoSMS", + "form": form, + }, + ) @login_required @lecturer_required def edit_post(request, pk): instance = get_object_or_404(NewsAndEvents, pk=pk) - if request.method == 'POST': + if request.method == "POST": form = NewsAndEventsForm(request.POST, instance=instance) - title = request.POST.get('title') + title = request.POST.get("title") if form.is_valid(): form.save() - messages.success(request, (title + ' has been updated.')) - return redirect('home') + messages.success(request, (title + " has been updated.")) + return redirect("home") else: - messages.error(request, 'Please correct the error(s) below.') + messages.error(request, "Please correct the error(s) below.") else: form = NewsAndEventsForm(instance=instance) - return render(request, 'app/post_add.html', { - 'title': 'Edit Post | DjangoSMS', - 'form': form, - }) + return render( + request, + "app/post_add.html", + { + "title": "Edit Post | DjangoSMS", + "form": form, + }, + ) @login_required @@ -69,8 +77,9 @@ def delete_post(request, pk): post = get_object_or_404(NewsAndEvents, pk=pk) title = post.title post.delete() - messages.success(request, (title + ' has been deleted.')) - return redirect('home') + messages.success(request, (title + " has been deleted.")) + return redirect("home") + # ######################################################## # Session @@ -78,21 +87,23 @@ def delete_post(request, pk): @login_required @lecturer_required def session_list_view(request): - """ Show list of all sessions """ - sessions = Session.objects.all().order_by('-is_current_session', '-session') - return render(request, 'app/session_list.html', {"sessions": sessions}) + """Show list of all sessions""" + sessions = Session.objects.all().order_by("-is_current_session", "-session") + return render(request, "app/session_list.html", {"sessions": sessions}) @login_required @lecturer_required def session_add_view(request): - """ check request method, if POST we add session otherwise show empty form """ - if request.method == 'POST': + """check request method, if POST we add session otherwise show empty form""" + if request.method == "POST": form = SessionForm(request.POST) if form.is_valid(): - data = form.data.get('is_current_session') # returns string of 'True' if the user selected Yes + data = form.data.get( + "is_current_session" + ) # returns string of 'True' if the user selected Yes print(data) - if data == 'true': + if data == "true": sessions = Session.objects.all() if sessions: for session in sessions: @@ -105,22 +116,22 @@ def session_add_view(request): form.save() else: form.save() - messages.success(request, 'Session added successfully. ') - return redirect('session_list') + messages.success(request, "Session added successfully. ") + return redirect("session_list") else: form = SessionForm() - return render(request, 'app/session_update.html', {'form': form}) + return render(request, "app/session_update.html", {"form": form}) @login_required @lecturer_required def session_update_view(request, pk): session = Session.objects.get(pk=pk) - if request.method == 'POST': + if request.method == "POST": form = SessionForm(request.POST, instance=session) - data = form.data.get('is_current_session') - if data == 'true': + data = form.data.get("is_current_session") + if data == "true": sessions = Session.objects.all() if sessions: for session in sessions: @@ -128,21 +139,21 @@ def session_update_view(request, pk): unset = Session.objects.get(is_current_session=True) unset.is_current_session = False unset.save() - + if form.is_valid(): form.save() - messages.success(request, 'Session updated successfully. ') - return redirect('session_list') + messages.success(request, "Session updated successfully. ") + return redirect("session_list") else: form = SessionForm(request.POST, instance=session) if form.is_valid(): form.save() - messages.success(request, 'Session updated successfully. ') - return redirect('session_list') + messages.success(request, "Session updated successfully. ") + return redirect("session_list") else: form = SessionForm(instance=session) - return render(request, 'app/session_update.html', {'form': form}) + return render(request, "app/session_update.html", {"form": form}) @login_required @@ -152,11 +163,13 @@ def session_delete_view(request, pk): if session.is_current_session: messages.error(request, "You cannot delete current session") - return redirect('session_list') + return redirect("session_list") else: session.delete() messages.success(request, "Session successfully deleted") - return redirect('session_list') + return redirect("session_list") + + # ######################################################## @@ -166,86 +179,106 @@ def session_delete_view(request, pk): @login_required @lecturer_required def semester_list_view(request): - semesters = Semester.objects.all().order_by('-is_current_semester', '-semester') - return render(request, 'app/semester_list.html', {"semesters": semesters, }) + semesters = Semester.objects.all().order_by("-is_current_semester", "-semester") + return render( + request, + "app/semester_list.html", + { + "semesters": semesters, + }, + ) @login_required @lecturer_required def semester_add_view(request): - if request.method == 'POST': + if request.method == "POST": form = SemesterForm(request.POST) if form.is_valid(): - data = form.data.get('is_current_semester') # returns string of 'True' if the user selected Yes - if data == 'True': - semester = form.data.get('semester') - ss = form.data.get('session') + data = form.data.get( + "is_current_semester" + ) # returns string of 'True' if the user selected Yes + if data == "True": + semester = form.data.get("semester") + ss = form.data.get("session") session = Session.objects.get(pk=ss) try: if Semester.objects.get(semester=semester, session=ss): - messages.error(request, semester + " semester in " + session.session + " session already exist") - return redirect('add_semester') + messages.error( + request, + semester + + " semester in " + + session.session + + " session already exist", + ) + return redirect("add_semester") except: semesters = Semester.objects.all() sessions = Session.objects.all() if semesters: for semester in semesters: if semester.is_current_semester == True: - unset_semester = Semester.objects.get(is_current_semester=True) + unset_semester = Semester.objects.get( + is_current_semester=True + ) unset_semester.is_current_semester = False unset_semester.save() for session in sessions: if session.is_current_session == True: - unset_session = Session.objects.get(is_current_session=True) + unset_session = Session.objects.get( + is_current_session=True + ) unset_session.is_current_session = False unset_session.save() - new_session = request.POST.get('session') + new_session = request.POST.get("session") set_session = Session.objects.get(pk=new_session) set_session.is_current_session = True set_session.save() form.save() - messages.success(request, 'Semester added successfully.') - return redirect('semester_list') + messages.success(request, "Semester added successfully.") + return redirect("semester_list") form.save() - messages.success(request, 'Semester added successfully. ') - return redirect('semester_list') + messages.success(request, "Semester added successfully. ") + return redirect("semester_list") else: form = SemesterForm() - return render(request, 'app/semester_update.html', {'form': form}) + return render(request, "app/semester_update.html", {"form": form}) @login_required @lecturer_required def semester_update_view(request, pk): semester = Semester.objects.get(pk=pk) - if request.method == 'POST': - if request.POST.get('is_current_semester') == 'True': # returns string of 'True' if the user selected yes for 'is current semester' + if request.method == "POST": + if ( + request.POST.get("is_current_semester") == "True" + ): # returns string of 'True' if the user selected yes for 'is current semester' unset_semester = Semester.objects.get(is_current_semester=True) unset_semester.is_current_semester = False unset_semester.save() unset_session = Session.objects.get(is_current_session=True) unset_session.is_current_session = False unset_session.save() - new_session = request.POST.get('session') + new_session = request.POST.get("session") form = SemesterForm(request.POST, instance=semester) if form.is_valid(): set_session = Session.objects.get(pk=new_session) set_session.is_current_session = True set_session.save() form.save() - messages.success(request, 'Semester updated successfully !') - return redirect('semester_list') + messages.success(request, "Semester updated successfully !") + return redirect("semester_list") else: form = SemesterForm(request.POST, instance=semester) if form.is_valid(): form.save() - return redirect('semester_list') + return redirect("semester_list") else: form = SemesterForm(instance=semester) - return render(request, 'app/semester_update.html', {'form': form}) + return render(request, "app/semester_update.html", {"form": form}) @login_required @@ -254,11 +287,13 @@ def semester_delete_view(request, pk): semester = get_object_or_404(Semester, pk=pk) if semester.is_current_semester: messages.error(request, "You cannot delete current semester") - return redirect('semester_list') + return redirect("semester_list") else: semester.delete() messages.success(request, "Semester successfully deleted") - return redirect('semester_list') + return redirect("semester_list") + + # ######################################################## @@ -284,7 +319,8 @@ def semester_delete_view(request, pk): # return response + @login_required @admin_required def dashboard_view(request): - return render(request, 'app/dashboard.html') \ No newline at end of file + return render(request, "app/dashboard.html") diff --git a/course/apps.py b/course/apps.py index 9e578cc..1e63067 100644 --- a/course/apps.py +++ b/course/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class CourseConfig(AppConfig): - name = 'course' + name = "course" diff --git a/course/decorators.py b/course/decorators.py index 89b163e..8365d6f 100644 --- a/course/decorators.py +++ b/course/decorators.py @@ -13,4 +13,4 @@ from coursemanagement.models import CourseSetting # return actual_decorator(function) # return actual_decorator -is_calender_on = CourseSetting.objects.filter(add_drop=True).count() > 0 \ No newline at end of file +is_calender_on = CourseSetting.objects.filter(add_drop=True).count() > 0 diff --git a/course/forms.py b/course/forms.py index 6d2274d..c9003ff 100644 --- a/course/forms.py +++ b/course/forms.py @@ -8,98 +8,109 @@ from .models import Program, Course, CourseAllocation, Upload, UploadVideo # User = settings.AUTH_USER_MODEL + class ProgramForm(forms.ModelForm): class Meta: model = Program - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['title'].widget.attrs.update({'class': 'form-control'}) - self.fields['summary'].widget.attrs.update({'class': 'form-control'}) + self.fields["title"].widget.attrs.update({"class": "form-control"}) + self.fields["summary"].widget.attrs.update({"class": "form-control"}) class CourseAddForm(forms.ModelForm): class Meta: model = Course - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['title'].widget.attrs.update({'class': 'form-control'}) - self.fields['code'].widget.attrs.update({'class': 'form-control'}) + self.fields["title"].widget.attrs.update({"class": "form-control"}) + self.fields["code"].widget.attrs.update({"class": "form-control"}) # self.fields['courseUnit'].widget.attrs.update({'class': 'form-control'}) - self.fields['credit'].widget.attrs.update({'class': 'form-control'}) - self.fields['summary'].widget.attrs.update({'class': 'form-control'}) - self.fields['program'].widget.attrs.update({'class': 'form-control'}) - self.fields['level'].widget.attrs.update({'class': 'form-control'}) - self.fields['year'].widget.attrs.update({'class': 'form-control'}) - self.fields['semester'].widget.attrs.update({'class': 'form-control'}) + self.fields["credit"].widget.attrs.update({"class": "form-control"}) + self.fields["summary"].widget.attrs.update({"class": "form-control"}) + self.fields["program"].widget.attrs.update({"class": "form-control"}) + self.fields["level"].widget.attrs.update({"class": "form-control"}) + self.fields["year"].widget.attrs.update({"class": "form-control"}) + self.fields["semester"].widget.attrs.update({"class": "form-control"}) class CourseAllocationForm(forms.ModelForm): courses = forms.ModelMultipleChoiceField( - queryset=Course.objects.all().order_by('level'), - widget=forms.CheckboxSelectMultiple(attrs={'class': 'browser-default checkbox'}), - required=True + queryset=Course.objects.all().order_by("level"), + widget=forms.CheckboxSelectMultiple( + attrs={"class": "browser-default checkbox"} + ), + required=True, ) lecturer = forms.ModelChoiceField( queryset=User.objects.filter(is_lecturer=True), - widget=forms.Select(attrs={'class': 'browser-default custom-select'}), + widget=forms.Select(attrs={"class": "browser-default custom-select"}), label="lecturer", ) class Meta: model = CourseAllocation - fields = ['lecturer', 'courses'] + fields = ["lecturer", "courses"] def __init__(self, *args, **kwargs): - user = kwargs.pop('user') + user = kwargs.pop("user") super(CourseAllocationForm, self).__init__(*args, **kwargs) - self.fields['lecturer'].queryset = User.objects.filter(is_lecturer=True) + self.fields["lecturer"].queryset = User.objects.filter(is_lecturer=True) class EditCourseAllocationForm(forms.ModelForm): courses = forms.ModelMultipleChoiceField( - queryset=Course.objects.all().order_by('level'), + queryset=Course.objects.all().order_by("level"), widget=forms.CheckboxSelectMultiple, - required=True + required=True, ) lecturer = forms.ModelChoiceField( queryset=User.objects.filter(is_lecturer=True), - widget=forms.Select(attrs={'class': 'browser-default custom-select'}), + widget=forms.Select(attrs={"class": "browser-default custom-select"}), label="lecturer", ) class Meta: model = CourseAllocation - fields = ['lecturer', 'courses'] + fields = ["lecturer", "courses"] def __init__(self, *args, **kwargs): # user = kwargs.pop('user') super(EditCourseAllocationForm, self).__init__(*args, **kwargs) - self.fields['lecturer'].queryset = User.objects.filter(is_lecturer=True) + self.fields["lecturer"].queryset = User.objects.filter(is_lecturer=True) # Upload files to specific course class UploadFormFile(forms.ModelForm): class Meta: model = Upload - fields = ('title', 'file', 'course',) + fields = ( + "title", + "file", + "course", + ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['title'].widget.attrs.update({'class': 'form-control'}) - self.fields['file'].widget.attrs.update({'class': 'form-control'}) + self.fields["title"].widget.attrs.update({"class": "form-control"}) + self.fields["file"].widget.attrs.update({"class": "form-control"}) # Upload video to specific course class UploadFormVideo(forms.ModelForm): class Meta: model = UploadVideo - fields = ('title', 'video', 'course',) + fields = ( + "title", + "video", + "course", + ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['title'].widget.attrs.update({'class': 'form-control'}) - self.fields['video'].widget.attrs.update({'class': 'form-control'}) + self.fields["title"].widget.attrs.update({"class": "form-control"}) + self.fields["video"].widget.attrs.update({"class": "form-control"}) diff --git a/course/models.py b/course/models.py index 51b544f..7bab27a 100644 --- a/course/models.py +++ b/course/models.py @@ -10,13 +10,13 @@ from .utils import * YEARS = ( - (1, '1'), - (2, '2'), - (3, '3'), - (4, '4'), - (4, '5'), - (4, '6'), - ) + (1, "1"), + (2, "2"), + (3, "3"), + (4, "4"), + (4, "5"), + (4, "6"), +) # LEVEL_COURSE = "Level course" BACHLOAR_DEGREE = "Bachloar" @@ -43,10 +43,10 @@ class ProgramManager(models.Manager): def search(self, query=None): qs = self.get_queryset() if query is not None: - or_lookup = (Q(title__icontains=query) | - Q(summary__icontains=query) - ) - qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups + or_lookup = Q(title__icontains=query) | Q(summary__icontains=query) + qs = qs.filter( + or_lookup + ).distinct() # distinct() is often necessary with Q lookups return qs @@ -60,19 +60,22 @@ class Program(models.Model): return self.title def get_absolute_url(self): - return reverse('program_detail', kwargs={'pk': self.pk}) + return reverse("program_detail", kwargs={"pk": self.pk}) class CourseManager(models.Manager): def search(self, query=None): qs = self.get_queryset() if query is not None: - or_lookup = (Q(title__icontains=query) | - Q(summary__icontains=query)| - Q(code__icontains=query)| - Q(slug__icontains=query) - ) - qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups + or_lookup = ( + Q(title__icontains=query) + | Q(summary__icontains=query) + | Q(code__icontains=query) + | Q(slug__icontains=query) + ) + qs = qs.filter( + or_lookup + ).distinct() # distinct() is often necessary with Q lookups return qs @@ -94,11 +97,12 @@ class Course(models.Model): return "{0} ({1})".format(self.title, self.code) def get_absolute_url(self): - return reverse('course_detail', kwargs={'slug': self.slug}) - + return reverse("course_detail", kwargs={"slug": self.slug}) + @property def is_current_semester(self): from app.models import Semester + current_semester = Semester.objects.get(is_current_semester=True) if self.semester == current_semester.semester: @@ -111,25 +115,50 @@ def course_pre_save_receiver(sender, instance, *args, **kwargs): if not instance.slug: instance.slug = unique_slug_generator(instance) + pre_save.connect(course_pre_save_receiver, sender=Course) class CourseAllocation(models.Model): - lecturer = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='allocated_lecturer') - courses = models.ManyToManyField(Course, related_name='allocated_course') - session = models.ForeignKey("app.Session", on_delete=models.CASCADE, blank=True, null=True) + lecturer = models.ForeignKey( + settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + related_name="allocated_lecturer", + ) + courses = models.ManyToManyField(Course, related_name="allocated_course") + session = models.ForeignKey( + "app.Session", on_delete=models.CASCADE, blank=True, null=True + ) def __str__(self): return self.lecturer.get_full_name def get_absolute_url(self): - return reverse('edit_allocated_course', kwargs={'pk': self.pk}) + return reverse("edit_allocated_course", kwargs={"pk": self.pk}) class Upload(models.Model): title = models.CharField(max_length=100) course = models.ForeignKey(Course, on_delete=models.CASCADE) - file = models.FileField(upload_to='course_files/', validators=[FileExtensionValidator(['pdf', 'docx', 'doc', 'xls', 'xlsx', 'ppt', 'pptx', 'zip', 'rar', '7zip'])]) + file = models.FileField( + upload_to="course_files/", + validators=[ + FileExtensionValidator( + [ + "pdf", + "docx", + "doc", + "xls", + "xlsx", + "ppt", + "pptx", + "zip", + "rar", + "7zip", + ] + ) + ], + ) updated_date = models.DateTimeField(auto_now=True, auto_now_add=False, null=True) upload_time = models.DateTimeField(auto_now=False, auto_now_add=True, null=True) @@ -138,18 +167,18 @@ class Upload(models.Model): def get_extension_short(self): ext = str(self.file).split(".") - ext = ext[len(ext)-1] + ext = ext[len(ext) - 1] - if ext == 'doc' or ext == 'docx': - return 'word' - elif ext == 'pdf': - return 'pdf' - elif ext == 'xls' or ext == 'xlsx': - return 'excel' - elif ext == 'ppt' or ext == 'pptx': - return 'powerpoint' - elif ext == 'zip' or ext == 'rar' or ext == '7zip': - return 'archive' + if ext == "doc" or ext == "docx": + return "word" + elif ext == "pdf": + return "pdf" + elif ext == "xls" or ext == "xlsx": + return "excel" + elif ext == "ppt" or ext == "pptx": + return "powerpoint" + elif ext == "zip" or ext == "rar" or ext == "7zip": + return "archive" def delete(self, *args, **kwargs): self.file.delete() @@ -160,7 +189,12 @@ class UploadVideo(models.Model): title = models.CharField(max_length=100) slug = models.SlugField(blank=True, unique=True) course = models.ForeignKey(Course, on_delete=models.CASCADE) - video = models.FileField(upload_to='course_videos/', validators=[FileExtensionValidator(['mp4', 'mkv', 'wmv', '3gp', 'f4v', 'avi', 'mp3'])]) + video = models.FileField( + upload_to="course_videos/", + validators=[ + FileExtensionValidator(["mp4", "mkv", "wmv", "3gp", "f4v", "avi", "mp3"]) + ], + ) summary = models.TextField(null=True, blank=True) timestamp = models.DateTimeField(auto_now=False, auto_now_add=True, null=True) @@ -168,7 +202,9 @@ class UploadVideo(models.Model): return str(self.title) def get_absolute_url(self): - return reverse('video_single', kwargs={'slug': self.course.slug, 'video_slug': self.slug}) + return reverse( + "video_single", kwargs={"slug": self.course.slug, "video_slug": self.slug} + ) def delete(self, *args, **kwargs): self.video.delete() @@ -179,12 +215,14 @@ def video_pre_save_receiver(sender, instance, *args, **kwargs): if not instance.slug: instance.slug = unique_slug_generator(instance) + pre_save.connect(video_pre_save_receiver, sender=UploadVideo) class CourseOffer(models.Model): - """NOTE: Only department head can offer semester courses""" - dep_head = models.ForeignKey("accounts.DepartmentHead", on_delete=models.CASCADE) + """NOTE: Only department head can offer semester courses""" - def __str__(self): - return "{}".format(self.dep_head) + dep_head = models.ForeignKey("accounts.DepartmentHead", on_delete=models.CASCADE) + + def __str__(self): + return "{}".format(self.dep_head) diff --git a/course/urls.py b/course/urls.py index 1ccccad..9da1327 100644 --- a/course/urls.py +++ b/course/urls.py @@ -4,38 +4,66 @@ from .views import * urlpatterns = [ # Program urls - path('', program_view, name='programs'), - path('/detail/', program_detail, name='program_detail'), - path('add/', program_add, name='add_program'), - path('/edit/', program_edit, name='edit_program'), - path('/delete/', program_delete, name='program_delete'), - + path("", program_view, name="programs"), + path("/detail/", program_detail, name="program_detail"), + path("add/", program_add, name="add_program"), + path("/edit/", program_edit, name="edit_program"), + path("/delete/", program_delete, name="program_delete"), # Course urls - path('course//detail/', course_single, name='course_detail'), - path('/course/add/', course_add, name='course_add'), - path('course//edit/', course_edit, name='edit_course'), - path('course/delete//', course_delete, name='delete_course'), - + path("course//detail/", course_single, name="course_detail"), + path("/course/add/", course_add, name="course_add"), + path("course//edit/", course_edit, name="edit_course"), + path("course/delete//", course_delete, name="delete_course"), # CourseAllocation urls - path('course/assign/', CourseAllocationFormView.as_view(), name='course_allocation'), - path('course/allocated/', course_allocation_view, name='course_allocation_view'), - path('allocated_course//edit/', edit_allocated_course, name='edit_allocated_course'), - path('course//deallocate/', deallocate_course, name='course_deallocate'), - + path( + "course/assign/", CourseAllocationFormView.as_view(), name="course_allocation" + ), + path("course/allocated/", course_allocation_view, name="course_allocation_view"), + path( + "allocated_course//edit/", + edit_allocated_course, + name="edit_allocated_course", + ), + path("course//deallocate/", deallocate_course, name="course_deallocate"), # File uploads urls - path('course//documentations/upload/', handle_file_upload, name='upload_file_view'), - path('course//documentations//edit/', handle_file_edit, name='upload_file_edit'), - path('course//documentations//delete/', handle_file_delete, name='upload_file_delete'), - + path( + "course//documentations/upload/", + handle_file_upload, + name="upload_file_view", + ), + path( + "course//documentations//edit/", + handle_file_edit, + name="upload_file_edit", + ), + path( + "course//documentations//delete/", + handle_file_delete, + name="upload_file_delete", + ), # Video uploads urls - path('course//video_tutorials/upload/', handle_video_upload, name='upload_video'), - path('course//video_tutorials//detail/', handle_video_single, name='video_single'), - path('course//video_tutorials//edit/', handle_video_edit, name='upload_video_edit'), - path('course//video_tutorials//delete/', handle_video_delete, name='upload_video_delete'), - + path( + "course//video_tutorials/upload/", + handle_video_upload, + name="upload_video", + ), + path( + "course//video_tutorials//detail/", + handle_video_single, + name="video_single", + ), + path( + "course//video_tutorials//edit/", + handle_video_edit, + name="upload_video_edit", + ), + path( + "course//video_tutorials//delete/", + handle_video_delete, + name="upload_video_delete", + ), # course registration - path('course/registration/', course_registration, name='course_registration'), - path('course/drop/', course_drop, name='course_drop'), - - path('my_courses/', user_course_list, name="user_course_list"), + path("course/registration/", course_registration, name="course_registration"), + path("course/drop/", course_drop, name="course_drop"), + path("my_courses/", user_course_list, name="user_course_list"), ] diff --git a/course/utils.py b/course/utils.py index 33dce5e..48d1108 100644 --- a/course/utils.py +++ b/course/utils.py @@ -1,4 +1,4 @@ -import datetime +import datetime import os import random import string @@ -7,12 +7,12 @@ from django.utils.text import slugify def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits): - return ''.join(random.choice(chars) for _ in range(size)) + return "".join(random.choice(chars) for _ in range(size)) def unique_slug_generator(instance, new_slug=None): """ - This is for a Django project and it assumes your instance + This is for a Django project and it assumes your instance has a model with a slug field and a title character (char) field. """ if new_slug is not None: @@ -24,8 +24,7 @@ def unique_slug_generator(instance, new_slug=None): qs_exists = Klass.objects.filter(slug=slug).exists() if qs_exists: new_slug = "{slug}-{randstr}".format( - slug=slug, - randstr=random_string_generator(size=4) - ) + slug=slug, randstr=random_string_generator(size=4) + ) return unique_slug_generator(instance, new_slug=new_slug) return slug diff --git a/course/views.py b/course/views.py index 2261cef..091bed8 100644 --- a/course/views.py +++ b/course/views.py @@ -13,8 +13,12 @@ from app.models import Session, Semester from result.models import TakenCourse from accounts.decorators import lecturer_required, student_required from .forms import ( - ProgramForm, CourseAddForm, CourseAllocationForm, - EditCourseAllocationForm, UploadFormFile, UploadFormVideo + ProgramForm, + CourseAddForm, + CourseAllocationForm, + EditCourseAllocationForm, + UploadFormFile, + UploadFormVideo, ) from .models import Program, Course, CourseAllocation, Upload, UploadVideo @@ -26,51 +30,67 @@ from .models import Program, Course, CourseAllocation, Upload, UploadVideo def program_view(request): programs = Program.objects.all() - program_filter = request.GET.get('program_filter') + program_filter = request.GET.get("program_filter") if program_filter: programs = Program.objects.filter(title__icontains=program_filter) - return render(request, 'course/program_list.html', { - 'title': "Programs | DjangoSMS", - 'programs': programs, - }) + return render( + request, + "course/program_list.html", + { + "title": "Programs | DjangoSMS", + "programs": programs, + }, + ) @login_required @lecturer_required def program_add(request): - if request.method == 'POST': + if request.method == "POST": form = ProgramForm(request.POST) if form.is_valid(): form.save() - messages.success(request, request.POST.get('title') + ' program has been created.') - return redirect('programs') + messages.success( + request, request.POST.get("title") + " program has been created." + ) + return redirect("programs") else: - messages.error(request, 'Correct the error(S) below.') + messages.error(request, "Correct the error(S) below.") else: form = ProgramForm() - return render(request, 'course/program_add.html', { - 'title': "Add Program | DjangoSMS", - 'form': form, - }) + return render( + request, + "course/program_add.html", + { + "title": "Add Program | DjangoSMS", + "form": form, + }, + ) @login_required def program_detail(request, pk): program = Program.objects.get(pk=pk) - courses = Course.objects.filter(program_id=pk).order_by('-year') - credits = Course.objects.aggregate(Sum('credit')) + courses = Course.objects.filter(program_id=pk).order_by("-year") + credits = Course.objects.aggregate(Sum("credit")) paginator = Paginator(courses, 10) - page = request.GET.get('page') + page = request.GET.get("page") courses = paginator.get_page(page) - return render(request, 'course/program_single.html', { - 'title': program.title, - 'program': program, 'courses': courses, 'credits': credits - }, ) + return render( + request, + "course/program_single.html", + { + "title": program.title, + "program": program, + "courses": courses, + "credits": credits, + }, + ) @login_required @@ -78,19 +98,22 @@ def program_detail(request, pk): def program_edit(request, pk): program = Program.objects.get(pk=pk) - if request.method == 'POST': + if request.method == "POST": form = ProgramForm(request.POST, instance=program) if form.is_valid(): form.save() - messages.success(request, str(request.POST.get('title')) + ' program has been updated.') - return redirect('programs') + messages.success( + request, str(request.POST.get("title")) + " program has been updated." + ) + return redirect("programs") else: form = ProgramForm(instance=program) - return render(request, 'course/program_add.html', { - 'title': "Edit Program | DjangoSMS", - 'form': form - }) + return render( + request, + "course/program_add.html", + {"title": "Edit Program | DjangoSMS", "form": form}, + ) @login_required @@ -99,11 +122,14 @@ def program_delete(request, pk): program = Program.objects.get(pk=pk) title = program.title program.delete() - messages.success(request, 'Program ' + title + ' has been deleted.') + messages.success(request, "Program " + title + " has been deleted.") + + return redirect("programs") + - return redirect('programs') # ######################################################## + # ######################################################## # Course views # ######################################################## @@ -116,61 +142,79 @@ def course_single(request, slug): # lecturers = User.objects.filter(allocated_lecturer__pk=course.id) lecturers = CourseAllocation.objects.filter(courses__pk=course.id) - return render(request, 'course/course_single.html', { - 'title': course.title, - 'course': course, - 'files': files, - 'videos': videos, - 'lecturers': lecturers, - 'media_url': settings.MEDIA_ROOT, - }, ) + return render( + request, + "course/course_single.html", + { + "title": course.title, + "course": course, + "files": files, + "videos": videos, + "lecturers": lecturers, + "media_url": settings.MEDIA_ROOT, + }, + ) @login_required @lecturer_required def course_add(request, pk): users = User.objects.all() - if request.method == 'POST': + if request.method == "POST": form = CourseAddForm(request.POST) - course_name = request.POST.get('title') - course_code = request.POST.get('code') + course_name = request.POST.get("title") + course_code = request.POST.get("code") if form.is_valid(): form.save() - messages.success(request, (course_name + '(' + course_code + ')' + ' has been created.')) - return redirect('program_detail', pk=request.POST.get('program')) + messages.success( + request, (course_name + "(" + course_code + ")" + " has been created.") + ) + return redirect("program_detail", pk=request.POST.get("program")) else: - messages.error(request, 'Correct the error(s) below.') + messages.error(request, "Correct the error(s) below.") else: - form = CourseAddForm(initial={'program': Program.objects.get(pk=pk)}) + form = CourseAddForm(initial={"program": Program.objects.get(pk=pk)}) - return render(request, 'course/course_add.html', { - 'title': "Add Course | DjangoSMS", - 'form': form, 'program': pk, 'users': users - }, ) + return render( + request, + "course/course_add.html", + { + "title": "Add Course | DjangoSMS", + "form": form, + "program": pk, + "users": users, + }, + ) @login_required @lecturer_required def course_edit(request, slug): course = get_object_or_404(Course, slug=slug) - if request.method == 'POST': + if request.method == "POST": form = CourseAddForm(request.POST, instance=course) - course_name = request.POST.get('title') - course_code = request.POST.get('code') + course_name = request.POST.get("title") + course_code = request.POST.get("code") if form.is_valid(): form.save() - messages.success(request, (course_name + '(' + course_code + ')' + ' has been updated.')) - return redirect('program_detail', pk=request.POST.get('program')) + messages.success( + request, (course_name + "(" + course_code + ")" + " has been updated.") + ) + return redirect("program_detail", pk=request.POST.get("program")) else: - messages.error(request, 'Correct the error(s) below.') + messages.error(request, "Correct the error(s) below.") else: form = CourseAddForm(instance=course) - return render(request, 'course/course_add.html', { - 'title': "Edit Course | DjangoSMS", - # 'form': form, 'program': pk, 'course': pk - 'form': form - }, ) + return render( + request, + "course/course_add.html", + { + "title": "Edit Course | DjangoSMS", + # 'form': form, 'program': pk, 'course': pk + "form": form, + }, + ) @login_required @@ -179,29 +223,31 @@ def course_delete(request, slug): course = Course.objects.get(slug=slug) # course_name = course.title course.delete() - messages.success(request, 'Course ' + course.title + ' has been deleted.') + messages.success(request, "Course " + course.title + " has been deleted.") + + return redirect("program_detail", pk=course.program.id) + - return redirect('program_detail', pk=course.program.id) # ######################################################## # ######################################################## # Course Allocation # ######################################################## -@method_decorator([login_required], name='dispatch') +@method_decorator([login_required], name="dispatch") class CourseAllocationFormView(CreateView): form_class = CourseAllocationForm - template_name = 'course/course_allocation_form.html' + template_name = "course/course_allocation_form.html" def get_form_kwargs(self): kwargs = super(CourseAllocationFormView, self).get_form_kwargs() - kwargs['user'] = self.request.user + kwargs["user"] = self.request.user return kwargs def form_valid(self, form): # if a staff has been allocated a course before update it else create new - lecturer = form.cleaned_data['lecturer'] - selected_courses = form.cleaned_data['courses'] + lecturer = form.cleaned_data["lecturer"] + selected_courses = form.cleaned_data["courses"] courses = () for course in selected_courses: courses += (course.pk,) @@ -214,40 +260,45 @@ class CourseAllocationFormView(CreateView): for i in range(0, selected_courses.count()): a.courses.add(courses[i]) a.save() - return redirect('course_allocation_view') + return redirect("course_allocation_view") def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context['title'] = "Assign Course | DjangoSMS" + context["title"] = "Assign Course | DjangoSMS" return context @login_required def course_allocation_view(request): allocated_courses = CourseAllocation.objects.all() - return render(request, 'course/course_allocation_view.html', { - 'title': "Course Allocations | DjangoSMS", - "allocated_courses": allocated_courses - }) + return render( + request, + "course/course_allocation_view.html", + { + "title": "Course Allocations | DjangoSMS", + "allocated_courses": allocated_courses, + }, + ) @login_required @lecturer_required def edit_allocated_course(request, pk): allocated = get_object_or_404(CourseAllocation, pk=pk) - if request.method == 'POST': + if request.method == "POST": form = EditCourseAllocationForm(request.POST, instance=allocated) if form.is_valid(): form.save() - messages.success(request, 'course assigned has been updated.') - return redirect('course_allocation_view') + messages.success(request, "course assigned has been updated.") + return redirect("course_allocation_view") else: form = EditCourseAllocationForm(instance=allocated) - return render(request, 'course/course_allocation_form.html', { - 'title': "Edit Course Allocated | DjangoSMS", - 'form': form, 'allocated': pk - }, ) + return render( + request, + "course/course_allocation_form.html", + {"title": "Edit Course Allocated | DjangoSMS", "form": form, "allocated": pk}, + ) @login_required @@ -255,8 +306,10 @@ def edit_allocated_course(request, pk): def deallocate_course(request, pk): course = CourseAllocation.objects.get(pk=pk) course.delete() - messages.success(request, 'successfully deallocate!') + messages.success(request, "successfully deallocate!") return redirect("course_allocation_view") + + # ######################################################## @@ -267,19 +320,22 @@ def deallocate_course(request, pk): @lecturer_required def handle_file_upload(request, slug): course = Course.objects.get(slug=slug) - if request.method == 'POST': - form = UploadFormFile(request.POST, request.FILES, {'course': course}) + if request.method == "POST": + form = UploadFormFile(request.POST, request.FILES, {"course": course}) # file_name = request.POST.get('name') if form.is_valid(): form.save() - messages.success(request, (request.POST.get('title') + ' has been uploaded.')) - return redirect('course_detail', slug=slug) + messages.success( + request, (request.POST.get("title") + " has been uploaded.") + ) + return redirect("course_detail", slug=slug) else: form = UploadFormFile() - return render(request, 'upload/upload_file_form.html', { - 'title': "File Upload | DjangoSMS", - 'form': form, 'course': course - }) + return render( + request, + "upload/upload_file_form.html", + {"title": "File Upload | DjangoSMS", "form": form, "course": course}, + ) @login_required @@ -287,19 +343,23 @@ def handle_file_upload(request, slug): def handle_file_edit(request, slug, file_id): course = Course.objects.get(slug=slug) instance = Upload.objects.get(pk=file_id) - if request.method == 'POST': + if request.method == "POST": form = UploadFormFile(request.POST, request.FILES, instance=instance) # file_name = request.POST.get('name') if form.is_valid(): form.save() - messages.success(request, (request.POST.get('title') + ' has been updated.')) - return redirect('course_detail', slug=slug) + messages.success( + request, (request.POST.get("title") + " has been updated.") + ) + return redirect("course_detail", slug=slug) else: form = UploadFormFile(instance=instance) - return render(request, 'upload/upload_file_form.html', { - 'title': instance.title, - 'form': form, 'course': course}) + return render( + request, + "upload/upload_file_form.html", + {"title": instance.title, "form": form, "course": course}, + ) def handle_file_delete(request, slug, file_id): @@ -307,8 +367,9 @@ def handle_file_delete(request, slug, file_id): # file_name = file.name file.delete() - messages.success(request, (file.title + ' has been deleted.')) - return redirect('course_detail', slug=slug) + messages.success(request, (file.title + " has been deleted.")) + return redirect("course_detail", slug=slug) + # ######################################################## # Video Upload views @@ -317,18 +378,21 @@ def handle_file_delete(request, slug, file_id): @lecturer_required def handle_video_upload(request, slug): course = Course.objects.get(slug=slug) - if request.method == 'POST': - form = UploadFormVideo(request.POST, request.FILES, {'course': course}) + if request.method == "POST": + form = UploadFormVideo(request.POST, request.FILES, {"course": course}) if form.is_valid(): form.save() - messages.success(request, (request.POST.get('title') + ' has been uploaded.')) - return redirect('course_detail', slug=slug) + messages.success( + request, (request.POST.get("title") + " has been uploaded.") + ) + return redirect("course_detail", slug=slug) else: form = UploadFormVideo() - return render(request, 'upload/upload_video_form.html', { - 'title': "Video Upload | DjangoSMS", - 'form': form, 'course': course - }) + return render( + request, + "upload/upload_video_form.html", + {"title": "Video Upload | DjangoSMS", "form": form, "course": course}, + ) @login_required @@ -336,7 +400,7 @@ def handle_video_upload(request, slug): def handle_video_single(request, slug, video_slug): course = get_object_or_404(Course, slug=slug) video = get_object_or_404(UploadVideo, slug=video_slug) - return render(request, 'upload/video_single.html', {'video': video}) + return render(request, "upload/video_single.html", {"video": video}) @login_required @@ -344,18 +408,22 @@ def handle_video_single(request, slug, video_slug): def handle_video_edit(request, slug, video_slug): course = Course.objects.get(slug=slug) instance = UploadVideo.objects.get(slug=video_slug) - if request.method == 'POST': + if request.method == "POST": form = UploadFormVideo(request.POST, request.FILES, instance=instance) if form.is_valid(): form.save() - messages.success(request, (request.POST.get('title') + ' has been updated.')) - return redirect('course_detail', slug=slug) + messages.success( + request, (request.POST.get("title") + " has been updated.") + ) + return redirect("course_detail", slug=slug) else: form = UploadFormVideo(instance=instance) - return render(request, 'upload/upload_video_form.html', { - 'title': instance.title, - 'form': form, 'course': course}) + return render( + request, + "upload/upload_video_form.html", + {"title": instance.title, "form": form, "course": course}, + ) def handle_video_delete(request, slug, video_slug): @@ -363,8 +431,10 @@ def handle_video_delete(request, slug, video_slug): # video = UploadVideo.objects.get(slug=video_slug) video.delete() - messages.success(request, (video.title + ' has been deleted.')) - return redirect('course_detail', slug=slug) + messages.success(request, (video.title + " has been deleted.")) + return redirect("course_detail", slug=slug) + + # ######################################################## @@ -374,10 +444,10 @@ def handle_video_delete(request, slug, video_slug): @login_required @student_required def course_registration(request): - if request.method == 'POST': + if request.method == "POST": ids = () data = request.POST.copy() - data.pop('csrfmiddlewaretoken', None) # remove csrf_token + data.pop("csrfmiddlewaretoken", None) # remove csrf_token for key in data.keys(): ids = ids + (str(key),) for s in range(0, len(ids)): @@ -385,8 +455,8 @@ def course_registration(request): course = Course.objects.get(pk=ids[s]) obj = TakenCourse.objects.create(student=student, course=course) obj.save() - messages.success(request, 'Courses Registered Successfully!') - return redirect('course_registration') + messages.success(request, "Courses Registered Successfully!") + return redirect("course_registration") else: # student = Student.objects.get(student__pk=request.user.id) student = get_object_or_404(Student, student__id=request.user.id) @@ -396,15 +466,26 @@ def course_registration(request): t += (i.course.pk,) current_semester = Semester.objects.get(is_current_semester=True) - courses = Course.objects.filter(program__pk=student.department.id, level=student.level, semester=current_semester - ).exclude(id__in=t).order_by('year') - all_courses = Course.objects.filter(level=student.level, program__pk=student.department.id) + courses = ( + Course.objects.filter( + program__pk=student.department.id, + level=student.level, + semester=current_semester, + ) + .exclude(id__in=t) + .order_by("year") + ) + all_courses = Course.objects.filter( + level=student.level, program__pk=student.department.id + ) no_course_is_registered = False # Check if no course is registered all_courses_are_registered = False registered_courses = Course.objects.filter(level=student.level).filter(id__in=t) - if registered_courses.count() == 0: # Check if number of registered courses is 0 + if ( + registered_courses.count() == 0 + ): # Check if number of registered courses is 0 no_course_is_registered = True if registered_courses.count() == all_courses.count(): @@ -432,16 +513,16 @@ def course_registration(request): "total_registered_credit": total_registered_credit, "student": student, } - return render(request, 'course/course_registration.html', context) + return render(request, "course/course_registration.html", context) @login_required @student_required def course_drop(request): - if request.method == 'POST': + if request.method == "POST": ids = () data = request.POST.copy() - data.pop('csrfmiddlewaretoken', None) # remove csrf_token + data.pop("csrfmiddlewaretoken", None) # remove csrf_token for key in data.keys(): ids = ids + (str(key),) for s in range(0, len(ids)): @@ -449,8 +530,10 @@ def course_drop(request): course = Course.objects.get(pk=ids[s]) obj = TakenCourse.objects.get(student=student, course=course) obj.delete() - messages.success(request, 'Successfully Dropped!') - return redirect('course_registration') + messages.success(request, "Successfully Dropped!") + return redirect("course_registration") + + # ######################################################## @@ -459,18 +542,22 @@ def user_course_list(request): if request.user.is_lecturer: courses = Course.objects.filter(allocated_course__lecturer__pk=request.user.id) - return render(request, 'course/user_course_list.html', {'courses': courses}) + return render(request, "course/user_course_list.html", {"courses": courses}) elif request.user.is_student: student = Student.objects.get(student__pk=request.user.id) - taken_courses = TakenCourse.objects.filter(student__student__id=student.student.id) - courses = Course.objects.filter(level=student.level).filter(program__pk=student.department.id) + taken_courses = TakenCourse.objects.filter( + student__student__id=student.student.id + ) + courses = Course.objects.filter(level=student.level).filter( + program__pk=student.department.id + ) - return render(request, 'course/user_course_list.html', { - 'student': student, - 'taken_courses': taken_courses, - 'courses': courses - }) + return render( + request, + "course/user_course_list.html", + {"student": student, "taken_courses": taken_courses, "courses": courses}, + ) else: - return render(request, 'course/user_course_list.html') + return render(request, "course/user_course_list.html") diff --git a/payments/apps.py b/payments/apps.py index 58d36ac..7eb624c 100644 --- a/payments/apps.py +++ b/payments/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class PaymentsConfig(AppConfig): - name = 'payments' + name = "payments" diff --git a/payments/models.py b/payments/models.py index 9952e56..f6ab91a 100644 --- a/payments/models.py +++ b/payments/models.py @@ -3,8 +3,8 @@ from django.conf import settings class Invoice(models.Model): - user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) - total = models.FloatField(null=True, blank=True) - amount = models.FloatField(null=True, blank=True) - payment_complete = models.BooleanField(default=False) - invoice_code = models.CharField(max_length=200, blank=True, null=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + total = models.FloatField(null=True, blank=True) + amount = models.FloatField(null=True, blank=True) + payment_complete = models.BooleanField(default=False) + invoice_code = models.CharField(max_length=200, blank=True, null=True) diff --git a/payments/urls.py b/payments/urls.py index 51419a7..04a7fb5 100644 --- a/payments/urls.py +++ b/payments/urls.py @@ -2,18 +2,15 @@ from django.urls import path from . import views urlpatterns = [ - path('', views.PaymentGetwaysView.as_view(), name='payment_gateways'), - - path('paypal/', views.payment_paypal, name="paypal"), - path('stripe/', views.payment_stripe, name="stripe"), - path('coinbase/', views.payment_coinbase, name="coinbase"), - path('paylike/', views.payment_paylike, name="paylike"), - - path('stripe-charge/', views.stripe_charge, name='stripe_charge'), - path('gopay-charge/', views.gopay_charge, name="gopay_charge"), - - path('payment-succeed/', views.payment_succeed, name="payment-succeed"), - path('complete/', views.paymentComplete, name="complete"), - path('create-invoice/', views.create_invoice, name="create_invoice"), - path('invoice-detail//', views.invoice_detail, name="invoice_detail"), + path("", views.PaymentGetwaysView.as_view(), name="payment_gateways"), + path("paypal/", views.payment_paypal, name="paypal"), + path("stripe/", views.payment_stripe, name="stripe"), + path("coinbase/", views.payment_coinbase, name="coinbase"), + path("paylike/", views.payment_paylike, name="paylike"), + path("stripe-charge/", views.stripe_charge, name="stripe_charge"), + path("gopay-charge/", views.gopay_charge, name="gopay_charge"), + path("payment-succeed/", views.payment_succeed, name="payment-succeed"), + path("complete/", views.paymentComplete, name="complete"), + path("create-invoice/", views.create_invoice, name="create_invoice"), + path("invoice-detail//", views.invoice_detail, name="invoice_detail"), ] diff --git a/payments/views.py b/payments/views.py index 749485c..c985301 100644 --- a/payments/views.py +++ b/payments/views.py @@ -16,155 +16,157 @@ from .models import Invoice def payment_paypal(request): - return render(request, 'payments/paypal.html', context={}) + return render(request, "payments/paypal.html", context={}) def payment_stripe(request): - return render(request, 'payments/stripe.html', context={}) + return render(request, "payments/stripe.html", context={}) def payment_coinbase(request): - return render(request, 'payments/coinbase.html', context={}) + return render(request, "payments/coinbase.html", context={}) def payment_paylike(request): - return render(request, 'payments/paylike.html', context={}) + return render(request, "payments/paylike.html", context={}) def payment_succeed(request): - return render(request, 'payments/payment_succeed.html', context={}) + return render(request, "payments/payment_succeed.html", context={}) class PaymentGetwaysView(TemplateView): - template_name = 'payments/payment_gateways.html' + template_name = "payments/payment_gateways.html" def get_context_data(self, **kwargs): context = super(PaymentGetwaysView, self).get_context_data(**kwargs) - context['key'] = settings.STRIPE_PUBLISHABLE_KEY - context['amount'] = 500 - context['description'] = "Stripe Payment" - context['invoice_session'] = self.request.session['invoice_session'] - print(context['invoice_session']) + context["key"] = settings.STRIPE_PUBLISHABLE_KEY + context["amount"] = 500 + context["description"] = "Stripe Payment" + context["invoice_session"] = self.request.session["invoice_session"] + print(context["invoice_session"]) return context def stripe_charge(request): stripe.api_key = settings.STRIPE_SECRET_KEY - if request.method == 'POST': + if request.method == "POST": charge = stripe.Charge.create( amount=500, - currency='eur', - description='A Django charge', - source=request.POST['stripeToken'] + currency="eur", + description="A Django charge", + source=request.POST["stripeToken"], ) - invoice_code = request.session['invoice_session'] + invoice_code = request.session["invoice_session"] invoice = Invoice.objects.get(invoice_code=invoice_code) invoice.payment_complete = True invoice.save() - return redirect('completed') + return redirect("completed") # return JsonResponse({"invoice_code": invoice.invoice_code}, status=201) # return render(request, 'payments/charge.html') def gopay_charge(request): - if request.method == 'POST': + if request.method == "POST": user = request.user - payments = gopay.payments({ - 'goid': '[PAYMENT_ID]', - 'clientId': '[GOPAY_CLIENT_ID]', - 'clientSecret': '[GOPAY_CLIENT_SECRET]', - 'isProductionMode': False, - 'scope': gopay.TokenScope.ALL, - 'language': gopay.Language.ENGLISH, - 'timeout': 30 - }) + payments = gopay.payments( + { + "goid": "[PAYMENT_ID]", + "clientId": "[GOPAY_CLIENT_ID]", + "clientSecret": "[GOPAY_CLIENT_SECRET]", + "isProductionMode": False, + "scope": gopay.TokenScope.ALL, + "language": gopay.Language.ENGLISH, + "timeout": 30, + } + ) # recurrent payment must have field '' recurrentPayment = { - 'recurrence': { - 'recurrence_cycle': Recurrence.DAILY, - 'recurrence_period': "7", - 'recurrence_date_to': '2015-12-31' + "recurrence": { + "recurrence_cycle": Recurrence.DAILY, + "recurrence_period": "7", + "recurrence_date_to": "2015-12-31", } } # pre-authorized payment must have field 'preauthorization' - preauthorizedPayment = { - 'preauthorization': True - } + preauthorizedPayment = {"preauthorization": True} - response = payments.create_payment({ - 'payer': { - 'default_payment_instrument': PaymentInstrument.BANK_ACCOUNT, - 'allowed_payment_instruments': [PaymentInstrument.BANK_ACCOUNT], - 'default_swift': BankSwiftCode.FIO_BANKA, - 'allowed_swifts': [BankSwiftCode.FIO_BANKA, BankSwiftCode.MBANK], - 'contact': { - 'first_name': user.first_name, - 'last_name': user.last_name, - 'email': user.email, - 'phone_number': user.phone, - 'city': 'example city', - 'street': 'Plana 67', - 'postal_code': '373 01', - 'country_code': 'CZE', + response = payments.create_payment( + { + "payer": { + "default_payment_instrument": PaymentInstrument.BANK_ACCOUNT, + "allowed_payment_instruments": [PaymentInstrument.BANK_ACCOUNT], + "default_swift": BankSwiftCode.FIO_BANKA, + "allowed_swifts": [BankSwiftCode.FIO_BANKA, BankSwiftCode.MBANK], + "contact": { + "first_name": user.first_name, + "last_name": user.last_name, + "email": user.email, + "phone_number": user.phone, + "city": "example city", + "street": "Plana 67", + "postal_code": "373 01", + "country_code": "CZE", + }, }, - }, - 'amount': 150, - 'currency': Currency.CZECH_CROWNS, - 'order_number': '001', - 'order_description': 'pojisteni01', - 'items': [ - {'name': 'item01', 'amount': 50}, - {'name': 'item02', 'amount': 100}, - ], - 'additional_params': [ - {'name': 'invoicenumber', 'value': '2015001003'} - ], - 'callback': { - 'return_url': 'http://www.your-url.tld/return', - 'notification_url': 'http://www.your-url.tld/notify' - }, - 'lang': Language.CZECH, # if lang is not specified, then default lang is used - }) + "amount": 150, + "currency": Currency.CZECH_CROWNS, + "order_number": "001", + "order_description": "pojisteni01", + "items": [ + {"name": "item01", "amount": 50}, + {"name": "item02", "amount": 100}, + ], + "additional_params": [{"name": "invoicenumber", "value": "2015001003"}], + "callback": { + "return_url": "http://www.your-url.tld/return", + "notification_url": "http://www.your-url.tld/notify", + }, + "lang": Language.CZECH, # if lang is not specified, then default lang is used + } + ) if response.has_succeed(): print("\nPayment Succeed\n") print("hooray, API returned " + str(response)) else: print("\nPayment Fail\n") - print("oops, API returned " + str(response.status_code) + ": " + str(response)) + print( + "oops, API returned " + str(response.status_code) + ": " + str(response) + ) return JsonResponse({"message": str(response)}) - + return JsonResponse({"message": "GET requested"}) def paymentComplete(request): print(request.is_ajax()) - if request.is_ajax() or request.method == 'POST': - invoice_id = request.session['invoice_session'] + if request.is_ajax() or request.method == "POST": + invoice_id = request.session["invoice_session"] invoice = Invoice.objects.get(id=invoice_id) invoice.payment_complete = True invoice.save() # return redirect('invoice', invoice.invoice_code) body = json.loads(request.body) - print('BODY:', body) - return JsonResponse('Payment completed!', safe=False) + print("BODY:", body) + return JsonResponse("Payment completed!", safe=False) def create_invoice(request): print(request.is_ajax()) - if request.method == 'POST': + if request.method == "POST": invoice = Invoice.objects.create( - user = request.user, - amount = request.POST.get('amount'), + user=request.user, + amount=request.POST.get("amount"), total=26, invoice_code=str(uuid.uuid4()), ) - request.session['invoice_session'] = invoice.invoice_code - return redirect('payment_gateways') + request.session["invoice_session"] = invoice.invoice_code + return redirect("payment_gateways") # if request.is_ajax(): # invoice = Invoice.objects.create( # user = request.user, @@ -173,12 +175,16 @@ def create_invoice(request): # ) # return JsonResponse({'invoice': invoice}, status=201) # created - return render(request, 'invoices.html', context={ - 'invoices': Invoice.objects.filter(user=request.user) - }) + return render( + request, + "invoices.html", + context={"invoices": Invoice.objects.filter(user=request.user)}, + ) def invoice_detail(request, slug): - return render(request, 'invoice_detail.html', context={ - 'invoice': Invoice.objects.get(invoice_code=slug) - }) + return render( + request, + "invoice_detail.html", + context={"invoice": Invoice.objects.get(invoice_code=slug)}, + ) diff --git a/quiz/admin.py b/quiz/admin.py index a0b684b..e023877 100644 --- a/quiz/admin.py +++ b/quiz/admin.py @@ -3,7 +3,15 @@ from django.contrib import admin from django.contrib.admin.widgets import FilteredSelectMultiple from django.utils.translation import gettext_lazy as _ -from .models import Quiz, Progress, Question, MCQuestion, Choice, Essay_Question, Sitting +from .models import ( + Quiz, + Progress, + Question, + MCQuestion, + Choice, + Essay_Question, + Sitting, +) class ChoiceInline(admin.TabularInline): @@ -11,7 +19,6 @@ class ChoiceInline(admin.TabularInline): class QuizAdminForm(forms.ModelForm): - class Meta: model = Quiz exclude = [] @@ -20,19 +27,20 @@ class QuizAdminForm(forms.ModelForm): queryset=Question.objects.all().select_subclasses(), required=False, label=_("Questions"), - widget=FilteredSelectMultiple( - verbose_name=_("Questions"), - is_stacked=False)) + widget=FilteredSelectMultiple(verbose_name=_("Questions"), is_stacked=False), + ) def __init__(self, *args, **kwargs): super(QuizAdminForm, self).__init__(*args, **kwargs) if self.instance.pk: - self.fields['questions'].initial = self.instance.question_set.all().select_subclasses() + self.fields[ + "questions" + ].initial = self.instance.question_set.all().select_subclasses() def save(self, commit=True): quiz = super(QuizAdminForm, self).save(commit=False) quiz.save() - quiz.question_set.set(self.cleaned_data['questions']) + quiz.question_set.set(self.cleaned_data["questions"]) self.save_m2m() return quiz @@ -40,32 +48,43 @@ class QuizAdminForm(forms.ModelForm): class QuizAdmin(admin.ModelAdmin): form = QuizAdminForm - list_display = ('title', ) + list_display = ("title",) # list_filter = ('category',) - search_fields = ('description', 'category', ) + search_fields = ( + "description", + "category", + ) class MCQuestionAdmin(admin.ModelAdmin): - list_display = ('content', ) + list_display = ("content",) # list_filter = ('category',) - fields = ('content', 'figure', 'quiz', 'explanation', 'choice_order') + fields = ("content", "figure", "quiz", "explanation", "choice_order") - search_fields = ('content', 'explanation') - filter_horizontal = ('quiz',) + search_fields = ("content", "explanation") + filter_horizontal = ("quiz",) inlines = [ChoiceInline] class ProgressAdmin(admin.ModelAdmin): - search_fields = ('user', 'score', ) + search_fields = ( + "user", + "score", + ) class EssayQuestionAdmin(admin.ModelAdmin): - list_display = ('content', ) + list_display = ("content",) # list_filter = ('category',) - fields = ('content', 'quiz', 'explanation', ) - search_fields = ('content', 'explanation') - filter_horizontal = ('quiz',) + fields = ( + "content", + "quiz", + "explanation", + ) + search_fields = ("content", "explanation") + filter_horizontal = ("quiz",) + admin.site.register(Quiz, QuizAdmin) admin.site.register(MCQuestion, MCQuestionAdmin) diff --git a/quiz/apps.py b/quiz/apps.py index eaa49da..0b66cef 100644 --- a/quiz/apps.py +++ b/quiz/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class QuizConfig(AppConfig): - name = 'quiz' + name = "quiz" diff --git a/quiz/forms.py b/quiz/forms.py index 0f4b6ab..b3d3a11 100644 --- a/quiz/forms.py +++ b/quiz/forms.py @@ -14,18 +14,20 @@ class QuestionForm(forms.Form): def __init__(self, question, *args, **kwargs): super(QuestionForm, self).__init__(*args, **kwargs) choice_list = [x for x in question.get_choices_list()] - self.fields["answers"] = forms.ChoiceField(choices=choice_list, widget=RadioSelect) + self.fields["answers"] = forms.ChoiceField( + choices=choice_list, widget=RadioSelect + ) class EssayForm(forms.Form): def __init__(self, question, *args, **kwargs): super(EssayForm, self).__init__(*args, **kwargs) self.fields["answers"] = forms.CharField( - widget=Textarea(attrs={'style': 'width:100%'})) + widget=Textarea(attrs={"style": "width:100%"}) + ) class QuizAddForm(forms.ModelForm): - class Meta: model = Quiz exclude = [] @@ -34,30 +36,35 @@ class QuizAddForm(forms.ModelForm): queryset=Question.objects.all().select_subclasses(), required=False, label=_("Questions"), - widget=FilteredSelectMultiple( - verbose_name=_("Questions"), - is_stacked=False)) + widget=FilteredSelectMultiple(verbose_name=_("Questions"), is_stacked=False), + ) def __init__(self, *args, **kwargs): super(QuizAddForm, self).__init__(*args, **kwargs) if self.instance.pk: - self.fields['questions'].initial = self.instance.question_set.all().select_subclasses() + self.fields[ + "questions" + ].initial = self.instance.question_set.all().select_subclasses() def save(self, commit=True): quiz = super(QuizAddForm, self).save(commit=False) quiz.save() - quiz.question_set.set(self.cleaned_data['questions']) + quiz.question_set.set(self.cleaned_data["questions"]) self.save_m2m() return quiz class MCQuestionForm(forms.ModelForm): - class Meta: model = MCQuestion exclude = () MCQuestionFormSet = inlineformset_factory( - MCQuestion, Choice, form=MCQuestionForm, fields=['choice', 'correct'], can_delete=True, extra=5 + MCQuestion, + Choice, + form=MCQuestionForm, + fields=["choice", "correct"], + can_delete=True, + extra=5, ) diff --git a/quiz/models.py b/quiz/models.py index 2b83970..46b28bb 100644 --- a/quiz/models.py +++ b/quiz/models.py @@ -4,7 +4,10 @@ import json from django.db import models from django.urls import reverse from django.core.exceptions import ValidationError, ImproperlyConfigured -from django.core.validators import (MaxValueValidator, validate_comma_separated_integer_list,) +from django.core.validators import ( + MaxValueValidator, + validate_comma_separated_integer_list, +) from django.utils.translation import gettext_lazy as _ from django.utils.timezone import now from django.conf import settings @@ -17,15 +20,15 @@ from course.models import Course from .utils import * CHOICE_ORDER_OPTIONS = ( - ('content', _('Content')), - ('random', _('Random')), - ('none', _('None')) + ("content", _("Content")), + ("random", _("Random")), + ("none", _("None")), ) CATEGORY_OPTIONS = ( - ('assignment', _('Assignment')), - ('exam', _('Exam')), - ('practice', _('Practice Quiz')) + ("assignment", _("Assignment")), + ("exam", _("Exam")), + ("practice", _("Practice Quiz")), ) @@ -33,12 +36,15 @@ class QuizManager(models.Manager): def search(self, query=None): qs = self.get_queryset() if query is not None: - or_lookup = (Q(title__icontains=query) | - Q(description__icontains=query)| - Q(category__icontains=query)| - Q(slug__icontains=query) - ) - qs = qs.filter(or_lookup).distinct() # distinct() is often necessary with Q lookups + or_lookup = ( + Q(title__icontains=query) + | Q(description__icontains=query) + | Q(category__icontains=query) + | Q(slug__icontains=query) + ) + qs = qs.filter( + or_lookup + ).distinct() # distinct() is often necessary with Q lookups return qs @@ -46,42 +52,76 @@ class Quiz(models.Model): course = models.ForeignKey(Course, on_delete=models.CASCADE, null=True) title = models.CharField(verbose_name=_("Title"), max_length=60, blank=False) slug = models.SlugField(blank=True, unique=True) - description = models.TextField(verbose_name=_("Description"), blank=True, help_text=_("a description of the quiz")) + description = models.TextField( + verbose_name=_("Description"), + blank=True, + help_text=_("a description of the quiz"), + ) category = models.TextField(choices=CATEGORY_OPTIONS, blank=True) - random_order = models.BooleanField(blank=False, default=False, verbose_name=_("Random Order"), - help_text=_("Display the questions in a random order or as they are set?")) + random_order = models.BooleanField( + blank=False, + default=False, + verbose_name=_("Random Order"), + help_text=_("Display the questions in a random order or as they are set?"), + ) - # max_questions = models.PositiveIntegerField(blank=True, null=True, verbose_name=_("Max Questions"), + # max_questions = models.PositiveIntegerField(blank=True, null=True, verbose_name=_("Max Questions"), # help_text=_("Number of questions to be answered on each attempt.")) - answers_at_end = models.BooleanField(blank=False, default=False, verbose_name=_("Answers at end"), - help_text=_("Correct answer is NOT shown after question. Answers displayed at the end.")) + answers_at_end = models.BooleanField( + blank=False, + default=False, + verbose_name=_("Answers at end"), + help_text=_( + "Correct answer is NOT shown after question. Answers displayed at the end." + ), + ) - exam_paper = models.BooleanField(blank=False, default=False, verbose_name=_("Exam Paper"), - help_text=_("If yes, the result of each attempt by a user will be stored. Necessary for marking.")) + exam_paper = models.BooleanField( + blank=False, + default=False, + verbose_name=_("Exam Paper"), + help_text=_( + "If yes, the result of each attempt by a user will be stored. Necessary for marking." + ), + ) - single_attempt = models.BooleanField(blank=False, default=False, verbose_name=_("Single Attempt"), - help_text=_("If yes, only one attempt by a user will be permitted.")) + single_attempt = models.BooleanField( + blank=False, + default=False, + verbose_name=_("Single Attempt"), + help_text=_("If yes, only one attempt by a user will be permitted."), + ) - pass_mark = models.SmallIntegerField(blank=True, default=50, verbose_name=_("Pass Mark"), validators=[MaxValueValidator(100)], - help_text=_("Percentage required to pass exam.")) + pass_mark = models.SmallIntegerField( + blank=True, + default=50, + verbose_name=_("Pass Mark"), + validators=[MaxValueValidator(100)], + help_text=_("Percentage required to pass exam."), + ) - draft = models.BooleanField(blank=True, default=False, verbose_name=_("Draft"), - help_text=_("If yes, the quiz is not displayed in the quiz list and can only be taken by users who can edit quizzes.")) + draft = models.BooleanField( + blank=True, + default=False, + verbose_name=_("Draft"), + help_text=_( + "If yes, the quiz is not displayed in the quiz list and can only be taken by users who can edit quizzes." + ), + ) timestamp = models.DateTimeField(auto_now=True) objects = QuizManager() def save(self, force_insert=False, force_update=False, *args, **kwargs): - if self.single_attempt is True: self.exam_paper = True if self.pass_mark > 100: - raise ValidationError('%s is above 100' % self.pass_mark) + raise ValidationError("%s is above 100" % self.pass_mark) if self.pass_mark < 0: - raise ValidationError('%s is below 0' % self.pass_mark) + raise ValidationError("%s is below 0" % self.pass_mark) super(Quiz, self).save(force_insert, force_update, *args, **kwargs) @@ -101,18 +141,18 @@ class Quiz(models.Model): def get_absolute_url(self): # return reverse('quiz_start_page', kwargs={'pk': self.pk}) - return reverse('quiz_index', kwargs={'slug': self.course.slug}) + return reverse("quiz_index", kwargs={"slug": self.course.slug}) def quiz_pre_save_receiver(sender, instance, *args, **kwargs): if not instance.slug: instance.slug = unique_slug_generator(instance) + pre_save.connect(quiz_pre_save_receiver, sender=Quiz) class ProgressManager(models.Manager): - def new_progress(self, user): new_progress = self.create(user=user, score="") new_progress.save() @@ -120,8 +160,14 @@ class ProgressManager(models.Manager): class Progress(models.Model): - user = models.OneToOneField(settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE) - score = models.CharField(max_length=1024, verbose_name=_("Score"), validators=[validate_comma_separated_integer_list]) + user = models.OneToOneField( + settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE + ) + score = models.CharField( + max_length=1024, + verbose_name=_("Score"), + validators=[validate_comma_separated_integer_list], + ) objects = ProgressManager() @@ -143,7 +189,17 @@ class Progress(models.Model): def update_score(self, question, score_to_add=0, possible_to_add=0): # category_test = Category.objects.filter(category=question.category).exists() - if any([item is False for item in [score_to_add, possible_to_add, isinstance(score_to_add, int), isinstance(possible_to_add, int)]]): + if any( + [ + item is False + for item in [ + score_to_add, + possible_to_add, + isinstance(score_to_add, int), + isinstance(possible_to_add, int), + ] + ] + ): return _("error"), _("category does not exist or invalid score") to_find = re.escape(str(question.quiz)) + r",(?P\d+),(?P\d+)," @@ -151,10 +207,12 @@ class Progress(models.Model): match = re.search(to_find, self.score, re.IGNORECASE) if match: - updated_score = int(match.group('score')) + abs(score_to_add) - updated_possible = int(match.group('possible')) + abs(possible_to_add) + updated_score = int(match.group("score")) + abs(score_to_add) + updated_possible = int(match.group("possible")) + abs(possible_to_add) - new_score = ",".join([str(question.quiz), str(updated_score), str(updated_possible), ""]) + new_score = ",".join( + [str(question.quiz), str(updated_score), str(updated_possible), ""] + ) # swap old score for the new one self.score = self.score.replace(match.group(), new_score) @@ -162,28 +220,33 @@ class Progress(models.Model): else: # if not present but existing, add with the points passed in - self.score += ",".join([str(question.quiz), str(score_to_add), str(possible_to_add), ""]) + self.score += ",".join( + [str(question.quiz), str(score_to_add), str(possible_to_add), ""] + ) self.save() def show_exams(self): if self.user.is_superuser: - return Sitting.objects.filter(complete=True).order_by('-end') + return Sitting.objects.filter(complete=True).order_by("-end") else: - return Sitting.objects.filter(user=self.user, complete=True).order_by('-end') + return Sitting.objects.filter(user=self.user, complete=True).order_by( + "-end" + ) class SittingManager(models.Manager): - def new_sitting(self, user, quiz, course): if quiz.random_order is True: - question_set = quiz.question_set.all().select_subclasses().order_by('?') + question_set = quiz.question_set.all().select_subclasses().order_by("?") else: question_set = quiz.question_set.all().select_subclasses() question_set = [item.id for item in question_set] if len(question_set) == 0: - raise ImproperlyConfigured('Question set of the quiz is empty. Please configure questions properly') + raise ImproperlyConfigured( + "Question set of the quiz is empty. Please configure questions properly" + ) # if quiz.max_questions and quiz.max_questions < len(question_set): # question_set = question_set[:quiz.max_questions] @@ -191,43 +254,70 @@ class SittingManager(models.Manager): questions = ",".join(map(str, question_set)) + "," new_sitting = self.create( - user=user, quiz=quiz, course=course, question_order=questions, - question_list=questions, incorrect_questions="", + user=user, + quiz=quiz, + course=course, + question_order=questions, + question_list=questions, + incorrect_questions="", current_score=0, complete=False, - user_answers='{}' + user_answers="{}", ) return new_sitting def user_sitting(self, user, quiz, course): - if quiz.single_attempt is True and self.filter(user=user, quiz=quiz, course=course, complete=True).exists(): + if ( + quiz.single_attempt is True + and self.filter(user=user, quiz=quiz, course=course, complete=True).exists() + ): return False try: sitting = self.get(user=user, quiz=quiz, course=course, complete=False) except Sitting.DoesNotExist: sitting = self.new_sitting(user, quiz, course) except Sitting.MultipleObjectsReturned: - sitting = self.filter(user=user, quiz=quiz, course=course, complete=False)[0] + sitting = self.filter(user=user, quiz=quiz, course=course, complete=False)[ + 0 + ] return sitting class Sitting(models.Model): - user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE + ) quiz = models.ForeignKey(Quiz, verbose_name=_("Quiz"), on_delete=models.CASCADE) - course = models.ForeignKey(Course, null=True, verbose_name=_("Course"), on_delete=models.CASCADE) + course = models.ForeignKey( + Course, null=True, verbose_name=_("Course"), on_delete=models.CASCADE + ) - question_order = models.CharField(max_length=1024, verbose_name=_("Question Order"), - validators=[validate_comma_separated_integer_list]) + question_order = models.CharField( + max_length=1024, + verbose_name=_("Question Order"), + validators=[validate_comma_separated_integer_list], + ) - question_list = models.CharField(max_length=1024, verbose_name=_("Question List"), - validators=[validate_comma_separated_integer_list]) + question_list = models.CharField( + max_length=1024, + verbose_name=_("Question List"), + validators=[validate_comma_separated_integer_list], + ) - incorrect_questions = models.CharField(max_length=1024, blank=True, verbose_name=_("Incorrect questions"), - validators=[validate_comma_separated_integer_list]) + incorrect_questions = models.CharField( + max_length=1024, + blank=True, + verbose_name=_("Incorrect questions"), + validators=[validate_comma_separated_integer_list], + ) current_score = models.IntegerField(verbose_name=_("Current Score")) - complete = models.BooleanField(default=False, blank=False, verbose_name=_("Complete")) - user_answers = models.TextField(blank=True, default='{}', verbose_name=_("User Answers")) + complete = models.BooleanField( + default=False, blank=False, verbose_name=_("Complete") + ) + user_answers = models.TextField( + blank=True, default="{}", verbose_name=_("User Answers") + ) start = models.DateTimeField(auto_now_add=True, verbose_name=_("Start")) end = models.DateTimeField(null=True, blank=True, verbose_name=_("End")) @@ -240,7 +330,7 @@ class Sitting(models.Model): if not self.question_list: return False - first, _ = self.question_list.split(',', 1) + first, _ = self.question_list.split(",", 1) question_id = int(first) return Question.objects.get_subclass(id=question_id) @@ -248,7 +338,7 @@ class Sitting(models.Model): if not self.question_list: return - _, others = self.question_list.split(',', 1) + _, others = self.question_list.split(",", 1) self.question_list = others self.save() @@ -261,14 +351,14 @@ class Sitting(models.Model): return self.current_score def _question_ids(self): - return [int(n) for n in self.question_order.split(',') if n] + return [int(n) for n in self.question_order.split(",") if n] @property def get_percent_correct(self): dividend = float(self.current_score) divisor = len(self._question_ids()) if divisor < 1: - return 0 # prevent divide by zero error + return 0 # prevent divide by zero error if dividend > divisor: return 100 @@ -287,7 +377,7 @@ class Sitting(models.Model): def add_incorrect_question(self, question): if len(self.incorrect_questions) > 0: - self.incorrect_questions += ',' + self.incorrect_questions += "," self.incorrect_questions += str(question.id) + "," if self.complete: self.add_to_score(-1) @@ -295,12 +385,12 @@ class Sitting(models.Model): @property def get_incorrect_questions(self): - return [int(q) for q in self.incorrect_questions.split(',') if q] + return [int(q) for q in self.incorrect_questions.split(",") if q] def remove_incorrect_question(self, question): current = self.get_incorrect_questions current.remove(question.id) - self.incorrect_questions = ','.join(map(str, current)) + self.incorrect_questions = ",".join(map(str, current)) self.add_to_score(1) self.save() @@ -323,7 +413,10 @@ class Sitting(models.Model): def get_questions(self, with_answers=False): question_ids = self._question_ids() - questions = sorted(self.quiz.question_set.filter(id__in=question_ids).select_subclasses(), key=lambda q: question_ids.index(q.id)) + questions = sorted( + self.quiz.question_set.filter(id__in=question_ids).select_subclasses(), + key=lambda q: question_ids.index(q.id), + ) if with_answers: user_answers = json.loads(self.user_answers) @@ -348,12 +441,21 @@ class Sitting(models.Model): class Question(models.Model): quiz = models.ManyToManyField(Quiz, verbose_name=_("Quiz"), blank=True) - figure = models.ImageField(upload_to='uploads/%Y/%m/%d', blank=True, null=True, verbose_name=_("Figure")) - content = models.CharField(max_length=1000, blank=False, - help_text=_("Enter the question text that you want displayed"), verbose_name=_('Question')) - explanation = models.TextField(max_length=2000, blank=True, + figure = models.ImageField( + upload_to="uploads/%Y/%m/%d", blank=True, null=True, verbose_name=_("Figure") + ) + content = models.CharField( + max_length=1000, + blank=False, + help_text=_("Enter the question text that you want displayed"), + verbose_name=_("Question"), + ) + explanation = models.TextField( + max_length=2000, + blank=True, help_text=_("Explanation to be shown after the question has been answered."), - verbose_name=_('Explanation')) + verbose_name=_("Explanation"), + ) objects = InheritanceManager() @@ -366,12 +468,16 @@ class Question(models.Model): class MCQuestion(Question): - choice_order = models.CharField( - max_length=30, null=True, blank=True, + max_length=30, + null=True, + blank=True, choices=CHOICE_ORDER_OPTIONS, - help_text=_("The order in which multichoice choice options are displayed to the user"), - verbose_name=_("Choice Order")) + help_text=_( + "The order in which multichoice choice options are displayed to the user" + ), + verbose_name=_("Choice Order"), + ) def check_if_correct(self, guess): answer = Choice.objects.get(id=guess) @@ -382,11 +488,11 @@ class MCQuestion(Question): return False def order_choices(self, queryset): - if self.choice_order == 'content': - return queryset.order_by('choice') - if self.choice_order == 'random': - return queryset.order_by('?') - if self.choice_order == 'none': + if self.choice_order == "content": + return queryset.order_by("choice") + if self.choice_order == "random": + return queryset.order_by("?") + if self.choice_order == "none": return queryset.order_by() return queryset @@ -394,8 +500,10 @@ class MCQuestion(Question): return self.order_choices(Choice.objects.filter(question=self)) def get_choices_list(self): - return [(choice.id, choice.choice) for choice in - self.order_choices(Choice.objects.filter(question=self))] + return [ + (choice.id, choice.choice) + for choice in self.order_choices(Choice.objects.filter(question=self)) + ] def answer_choice_to_string(self, guess): return Choice.objects.get(id=guess).choice @@ -406,15 +514,23 @@ class MCQuestion(Question): class Choice(models.Model): - question = models.ForeignKey(MCQuestion, verbose_name=_("Question"), on_delete=models.CASCADE) + question = models.ForeignKey( + MCQuestion, verbose_name=_("Question"), on_delete=models.CASCADE + ) - choice = models.CharField(max_length=1000, blank=False, - help_text=_("Enter the choice text that you want displayed"), - verbose_name=_("Content")) + choice = models.CharField( + max_length=1000, + blank=False, + help_text=_("Enter the choice text that you want displayed"), + verbose_name=_("Content"), + ) - correct = models.BooleanField(blank=False, default=False, - help_text=_("Is this a correct answer?"), - verbose_name=_("Correct")) + correct = models.BooleanField( + blank=False, + default=False, + help_text=_("Is this a correct answer?"), + verbose_name=_("Correct"), + ) def __str__(self): return self.choice @@ -425,7 +541,6 @@ class Choice(models.Model): class Essay_Question(Question): - def check_if_correct(self, guess): return False diff --git a/quiz/urls.py b/quiz/urls.py index 3f20b2b..6c73580 100644 --- a/quiz/urls.py +++ b/quiz/urls.py @@ -2,21 +2,23 @@ from django.urls import path from .views import * urlpatterns = [ - - path('/quizzes/', quiz_list, name='quiz_index'), - - path('progress/', view=QuizUserProgressView.as_view(), name='quiz_progress'), - + path("/quizzes/", quiz_list, name="quiz_index"), + path("progress/", view=QuizUserProgressView.as_view(), name="quiz_progress"), # path('marking//', view=QuizMarkingList.as_view(), name='quiz_marking'), - path('marking_list/', view=QuizMarkingList.as_view(), name='quiz_marking'), - - path('marking//', view=QuizMarkingDetail.as_view(), name='quiz_marking_detail'), - - path('//take/', view=QuizTake.as_view(), name='quiz_take'), - - path('/quiz_add/', QuizCreateView.as_view(), name='quiz_create'), - path('//add/', QuizUpdateView.as_view(), name='quiz_update'), - path('//delete/', quiz_delete, name='quiz_delete'), - path('mc-question/add///', MCQuestionCreate.as_view(), name='mc_create'), + path("marking_list/", view=QuizMarkingList.as_view(), name="quiz_marking"), + path( + "marking//", + view=QuizMarkingDetail.as_view(), + name="quiz_marking_detail", + ), + path("//take/", view=QuizTake.as_view(), name="quiz_take"), + path("/quiz_add/", QuizCreateView.as_view(), name="quiz_create"), + path("//add/", QuizUpdateView.as_view(), name="quiz_update"), + path("//delete/", quiz_delete, name="quiz_delete"), + path( + "mc-question/add///", + MCQuestionCreate.as_view(), + name="mc_create", + ), # path('mc-question/add///', MCQuestionCreate.as_view(), name='mc_create'), ] diff --git a/quiz/utils.py b/quiz/utils.py index 33dce5e..48d1108 100644 --- a/quiz/utils.py +++ b/quiz/utils.py @@ -1,4 +1,4 @@ -import datetime +import datetime import os import random import string @@ -7,12 +7,12 @@ from django.utils.text import slugify def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits): - return ''.join(random.choice(chars) for _ in range(size)) + return "".join(random.choice(chars) for _ in range(size)) def unique_slug_generator(instance, new_slug=None): """ - This is for a Django project and it assumes your instance + This is for a Django project and it assumes your instance has a model with a slug field and a title character (char) field. """ if new_slug is not None: @@ -24,8 +24,7 @@ def unique_slug_generator(instance, new_slug=None): qs_exists = Klass.objects.filter(slug=slug).exists() if qs_exists: new_slug = "{slug}-{randstr}".format( - slug=slug, - randstr=random_string_generator(size=4) - ) + slug=slug, randstr=random_string_generator(size=4) + ) return unique_slug_generator(instance, new_slug=new_slug) return slug diff --git a/quiz/views.py b/quiz/views.py index 53be81d..0887ab4 100644 --- a/quiz/views.py +++ b/quiz/views.py @@ -4,7 +4,16 @@ from django.contrib.auth.decorators import login_required, permission_required from django.core.exceptions import PermissionDenied from django.shortcuts import get_object_or_404, render, redirect from django.utils.decorators import method_decorator -from django.views.generic import DetailView, ListView, TemplateView, FormView, CreateView, FormView, DeleteView, UpdateView +from django.views.generic import ( + DetailView, + ListView, + TemplateView, + FormView, + CreateView, + FormView, + DeleteView, + UpdateView, +) from django.contrib import messages from django.urls import reverse_lazy from django.db import transaction @@ -16,58 +25,62 @@ from .models import * from .forms import * -@method_decorator([login_required, lecturer_required], name='dispatch') +@method_decorator([login_required, lecturer_required], name="dispatch") class QuizCreateView(CreateView): model = Quiz form_class = QuizAddForm def get_context_data(self, *args, **kwargs): context = super(QuizCreateView, self).get_context_data(**kwargs) - context['course'] = Course.objects.get(slug=self.kwargs['slug']) + context["course"] = Course.objects.get(slug=self.kwargs["slug"]) if self.request.POST: - context['form'] = QuizAddForm(self.request.POST) + context["form"] = QuizAddForm(self.request.POST) # context['quiz'] = self.request.POST.get('quiz') else: - context['form'] = QuizAddForm(initial={'course': Course.objects.get(slug=self.kwargs['slug'])}) + context["form"] = QuizAddForm( + initial={"course": Course.objects.get(slug=self.kwargs["slug"])} + ) return context def form_valid(self, form, **kwargs): context = self.get_context_data() - form = context['form'] + form = context["form"] with transaction.atomic(): self.object = form.save() if form.is_valid(): form.instance = self.object form.save() - return redirect('mc_create', slug=self.kwargs['slug'], quiz_id=form.instance.id) + return redirect( + "mc_create", slug=self.kwargs["slug"], quiz_id=form.instance.id + ) return super(QuizCreateView, self).form_invalid(form) -@method_decorator([login_required, lecturer_required], name='dispatch') +@method_decorator([login_required, lecturer_required], name="dispatch") class QuizUpdateView(UpdateView): model = Quiz form_class = QuizAddForm def get_context_data(self, *args, **kwargs): context = super(QuizUpdateView, self).get_context_data(**kwargs) - context['course'] = Course.objects.get(slug=self.kwargs['slug']) - quiz = Quiz.objects.get(pk=self.kwargs['pk']) + context["course"] = Course.objects.get(slug=self.kwargs["slug"]) + quiz = Quiz.objects.get(pk=self.kwargs["pk"]) if self.request.POST: - context['form'] = QuizAddForm(self.request.POST, instance=quiz) + context["form"] = QuizAddForm(self.request.POST, instance=quiz) else: - context['form'] = QuizAddForm(instance=quiz) + context["form"] = QuizAddForm(instance=quiz) return context def form_valid(self, form, **kwargs): context = self.get_context_data() - course = context['course'] - form = context['form'] + course = context["course"] + form = context["form"] with transaction.atomic(): self.object = form.save() if form.is_valid(): form.instance = self.object form.save() - return redirect('quiz_index', course.slug) + return redirect("quiz_index", course.slug) return super(QuizUpdateView, self).form_invalid(form) @@ -77,54 +90,62 @@ def quiz_delete(request, slug, pk): quiz = Quiz.objects.get(pk=pk) course = Course.objects.get(slug=slug) quiz.delete() - messages.success(request, f'successfuly deleted.') - return redirect('quiz_index', quiz.course.slug) + messages.success(request, f"successfuly deleted.") + return redirect("quiz_index", quiz.course.slug) -@method_decorator([login_required, lecturer_required], name='dispatch') +@method_decorator([login_required, lecturer_required], name="dispatch") class MCQuestionCreate(CreateView): model = MCQuestion form_class = MCQuestionForm def get_context_data(self, **kwargs): context = super(MCQuestionCreate, self).get_context_data(**kwargs) - context['course'] = Course.objects.get(slug=self.kwargs['slug']) - context['quiz_obj'] = Quiz.objects.get(id=self.kwargs['quiz_id']) - context['quizQuestions'] = Question.objects.filter(quiz=self.kwargs['quiz_id']).count() + context["course"] = Course.objects.get(slug=self.kwargs["slug"]) + context["quiz_obj"] = Quiz.objects.get(id=self.kwargs["quiz_id"]) + context["quizQuestions"] = Question.objects.filter( + quiz=self.kwargs["quiz_id"] + ).count() if self.request.POST: - context['form'] = MCQuestionForm(self.request.POST) - context['formset'] = MCQuestionFormSet(self.request.POST) + context["form"] = MCQuestionForm(self.request.POST) + context["formset"] = MCQuestionFormSet(self.request.POST) else: - context['form'] = MCQuestionForm(initial={'quiz': self.kwargs['quiz_id']}) - context['formset'] = MCQuestionFormSet() + context["form"] = MCQuestionForm(initial={"quiz": self.kwargs["quiz_id"]}) + context["formset"] = MCQuestionFormSet() return context def form_valid(self, form): context = self.get_context_data() - formset = context['formset'] - course = context['course'] + formset = context["formset"] + course = context["course"] with transaction.atomic(): - form.instance.question = self.request.POST.get('content') + form.instance.question = self.request.POST.get("content") self.object = form.save() if formset.is_valid(): formset.instance = self.object formset.save() if "another" in self.request.POST: - return redirect('mc_create', slug=self.kwargs['slug'], quiz_id=self.kwargs['quiz_id']) - return redirect('quiz_index', course.slug) + return redirect( + "mc_create", + slug=self.kwargs["slug"], + quiz_id=self.kwargs["quiz_id"], + ) + return redirect("quiz_index", course.slug) return super(MCQuestionCreate, self).form_invalid(form) @login_required def quiz_list(request, slug): - quizzes = Quiz.objects.filter(course__slug = slug).order_by('-timestamp') - course = Course.objects.get(slug = slug) - return render(request, 'quiz/quiz_list.html', {'quizzes': quizzes, 'course': course}) + quizzes = Quiz.objects.filter(course__slug=slug).order_by("-timestamp") + course = Course.objects.get(slug=slug) + return render( + request, "quiz/quiz_list.html", {"quizzes": quizzes, "course": course} + ) # return render(request, 'quiz/quiz_list.html', {'quizzes': quizzes}) -@method_decorator([login_required, lecturer_required], name='dispatch') +@method_decorator([login_required, lecturer_required], name="dispatch") class QuizMarkerMixin(object): @method_decorator(login_required) # @method_decorator(permission_required('quiz.view_sittings')) @@ -136,16 +157,16 @@ class QuizMarkerMixin(object): class SittingFilterTitleMixin(object): def get_queryset(self): queryset = super(SittingFilterTitleMixin, self).get_queryset() - quiz_filter = self.request.GET.get('quiz_filter') + quiz_filter = self.request.GET.get("quiz_filter") if quiz_filter: queryset = queryset.filter(quiz__title__icontains=quiz_filter) return queryset -@method_decorator([login_required], name='dispatch') +@method_decorator([login_required], name="dispatch") class QuizUserProgressView(TemplateView): - template_name = 'progress.html' + template_name = "progress.html" def dispatch(self, request, *args, **kwargs): return super(QuizUserProgressView, self).dispatch(request, *args, **kwargs) @@ -153,16 +174,19 @@ class QuizUserProgressView(TemplateView): def get_context_data(self, **kwargs): context = super(QuizUserProgressView, self).get_context_data(**kwargs) progress, c = Progress.objects.get_or_create(user=self.request.user) - context['cat_scores'] = progress.list_all_cat_scores - context['exams'] = progress.show_exams() - context['exams_counter'] = progress.show_exams().count() + context["cat_scores"] = progress.list_all_cat_scores + context["exams"] = progress.show_exams() + context["exams_counter"] = progress.show_exams().count() return context + from result.models import TakenCourse -@method_decorator([login_required, lecturer_required], name='dispatch') + +@method_decorator([login_required, lecturer_required], name="dispatch") class QuizMarkingList(QuizMarkerMixin, SittingFilterTitleMixin, ListView): model = Sitting + # def get_context_data(self, **kwargs): # context = super(QuizMarkingList, self).get_context_data(**kwargs) # context['queryset_counter'] = super(QuizMarkingList, self).get_queryset().filter(complete=True).filter(course__allocated_course__lecturer__pk=self.request.user.id).count() @@ -172,24 +196,31 @@ class QuizMarkingList(QuizMarkerMixin, SittingFilterTitleMixin, ListView): if self.request.user.is_superuser: queryset = super(QuizMarkingList, self).get_queryset().filter(complete=True) else: - queryset = super(QuizMarkingList, self).get_queryset().filter(quiz__course__allocated_course__lecturer__pk=self.request.user.id).filter(complete=True) + queryset = ( + super(QuizMarkingList, self) + .get_queryset() + .filter( + quiz__course__allocated_course__lecturer__pk=self.request.user.id + ) + .filter(complete=True) + ) # search by user - user_filter = self.request.GET.get('user_filter') + user_filter = self.request.GET.get("user_filter") if user_filter: queryset = queryset.filter(user__username__icontains=user_filter) - + return queryset -@method_decorator([login_required, lecturer_required], name='dispatch') +@method_decorator([login_required, lecturer_required], name="dispatch") class QuizMarkingDetail(QuizMarkerMixin, DetailView): model = Sitting def post(self, request, *args, **kwargs): sitting = self.get_object() - q_to_toggle = request.POST.get('qid', None) + q_to_toggle = request.POST.get("qid", None) if q_to_toggle: q = Question.objects.get_subclass(id=int(q_to_toggle)) if int(q_to_toggle) in sitting.get_incorrect_questions: @@ -201,37 +232,42 @@ class QuizMarkingDetail(QuizMarkerMixin, DetailView): def get_context_data(self, **kwargs): context = super(QuizMarkingDetail, self).get_context_data(**kwargs) - context['questions'] = context['sitting'].get_questions(with_answers=True) + context["questions"] = context["sitting"].get_questions(with_answers=True) return context # @method_decorator([login_required, student_required], name='dispatch') -@method_decorator([login_required], name='dispatch') +@method_decorator([login_required], name="dispatch") class QuizTake(FormView): form_class = QuestionForm - template_name = 'question.html' - result_template_name = 'result.html' + template_name = "question.html" + result_template_name = "result.html" # single_complete_template_name = 'single_complete.html' def dispatch(self, request, *args, **kwargs): - self.quiz = get_object_or_404(Quiz, slug=self.kwargs['slug']) - self.course = get_object_or_404(Course, pk=self.kwargs['pk']) + self.quiz = get_object_or_404(Quiz, slug=self.kwargs["slug"]) + self.course = get_object_or_404(Course, pk=self.kwargs["pk"]) quizQuestions = Question.objects.filter(quiz=self.quiz).count() - course = get_object_or_404(Course, pk=self.kwargs['pk']) + course = get_object_or_404(Course, pk=self.kwargs["pk"]) if quizQuestions <= 0: - messages.warning(request, f'Question set of the quiz is empty. try later!') - return redirect('quiz_index', self.course.slug) + messages.warning(request, f"Question set of the quiz is empty. try later!") + return redirect("quiz_index", self.course.slug) - if self.quiz.draft and not request.user.has_perm('quiz.change_quiz'): + if self.quiz.draft and not request.user.has_perm("quiz.change_quiz"): raise PermissionDenied - self.sitting = Sitting.objects.user_sitting(request.user, self.quiz, self.course) + self.sitting = Sitting.objects.user_sitting( + request.user, self.quiz, self.course + ) if self.sitting is False: # return render(request, self.single_complete_template_name) - messages.info(request, f'You have already sat this exam and only one sitting is permitted') - return redirect('quiz_index', self.course.slug) + messages.info( + request, + f"You have already sat this exam and only one sitting is permitted", + ) + return redirect("quiz_index", self.course.slug) return super(QuizTake, self).dispatch(request, *args, **kwargs) @@ -262,18 +298,18 @@ class QuizTake(FormView): def get_context_data(self, **kwargs): context = super(QuizTake, self).get_context_data(**kwargs) - context['question'] = self.question - context['quiz'] = self.quiz - context['course'] = get_object_or_404(Course, pk=self.kwargs['pk']) - if hasattr(self, 'previous'): - context['previous'] = self.previous - if hasattr(self, 'progress'): - context['progress'] = self.progress + context["question"] = self.question + context["quiz"] = self.quiz + context["course"] = get_object_or_404(Course, pk=self.kwargs["pk"]) + if hasattr(self, "previous"): + context["previous"] = self.previous + if hasattr(self, "progress"): + context["progress"] = self.progress return context def form_valid_user(self, form): progress, c = Progress.objects.get_or_create(user=self.request.user) - guess = form.cleaned_data['answers'] + guess = form.cleaned_data["answers"] is_correct = self.question.check_if_correct(guess) if is_correct is True: @@ -285,11 +321,11 @@ class QuizTake(FormView): if self.quiz.answers_at_end is not True: self.previous = { - 'previous_answer': guess, - 'previous_outcome': is_correct, - 'previous_question': self.question, - 'answers': self.question.get_choices(), - 'question_type': {self.question.__class__.__name__: True} + "previous_answer": guess, + "previous_outcome": is_correct, + "previous_question": self.question, + "answers": self.question.get_choices(), + "question_type": {self.question.__class__.__name__: True}, } else: self.previous = {} @@ -299,23 +335,27 @@ class QuizTake(FormView): def final_result_user(self): results = { - 'course': get_object_or_404(Course, pk=self.kwargs['pk']), - 'quiz': self.quiz, - 'score': self.sitting.get_current_score, - 'max_score': self.sitting.get_max_score, - 'percent': self.sitting.get_percent_correct, - 'sitting': self.sitting, - 'previous': self.previous, - 'course': get_object_or_404(Course, pk=self.kwargs['pk']) + "course": get_object_or_404(Course, pk=self.kwargs["pk"]), + "quiz": self.quiz, + "score": self.sitting.get_current_score, + "max_score": self.sitting.get_max_score, + "percent": self.sitting.get_percent_correct, + "sitting": self.sitting, + "previous": self.previous, + "course": get_object_or_404(Course, pk=self.kwargs["pk"]), } self.sitting.mark_quiz_complete() if self.quiz.answers_at_end: - results['questions'] = self.sitting.get_questions(with_answers=True) - results['incorrect_questions'] = self.sitting.get_incorrect_questions + results["questions"] = self.sitting.get_questions(with_answers=True) + results["incorrect_questions"] = self.sitting.get_incorrect_questions - if self.quiz.exam_paper is False or self.request.user.is_superuser or self.request.user.is_lecturer : + if ( + self.quiz.exam_paper is False + or self.request.user.is_superuser + or self.request.user.is_lecturer + ): self.sitting.delete() return render(self.request, self.result_template_name, results) diff --git a/result/admin.py b/result/admin.py index d93129a..c79fb16 100644 --- a/result/admin.py +++ b/result/admin.py @@ -6,8 +6,16 @@ from .models import TakenCourse, Result class ScoreAdmin(admin.ModelAdmin): list_display = [ - 'student', 'course', 'assignment', 'mid_exam', 'quiz', - 'attendance', 'final_exam', 'total', 'grade', 'comment' + "student", + "course", + "assignment", + "mid_exam", + "quiz", + "attendance", + "final_exam", + "total", + "grade", + "comment", ] diff --git a/result/apps.py b/result/apps.py index 71c04bb..8afd05c 100644 --- a/result/apps.py +++ b/result/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class ResultConfig(AppConfig): - name = 'result' + name = "result" diff --git a/result/models.py b/result/models.py index d5d7fcf..39973b8 100644 --- a/result/models.py +++ b/result/models.py @@ -6,13 +6,13 @@ from app.models import Session, Semester from course.models import Course YEARS = ( - (1, '1'), - (2, '2'), - (3, '3'), - (4, '4'), - (4, '5'), - (4, '6'), - ) + (1, "1"), + (2, "2"), + (3, "3"), + (4, "4"), + (4, "5"), + (4, "6"), +) # LEVEL_COURSE = "Level course" BACHLOAR_DEGREE = "Bachloar" @@ -48,18 +48,18 @@ F = "F" NG = "NG" GRADE = ( - (A_plus, "A+"), - (A, "A"), - (A_minus, "A-"), - (B_plus, "B+"), - (B, "B"), - (B_minus, "B-"), - (C_plus, "C+"), - (C, "C"), - (C_minus, "C-"), - (D, "D"), - (F, "F"), - (NG, "NG"), + (A_plus, "A+"), + (A, "A"), + (A_minus, "A-"), + (B_plus, "B+"), + (B, "B"), + (B_minus, "B-"), + (C_plus, "C+"), + (C, "C"), + (C_minus, "C-"), + (D, "D"), + (F, "F"), + (NG, "NG"), ) PASS = "PASS" @@ -84,7 +84,7 @@ class TakenCourseManager(models.Manager): else: cart_obj = Cart.objects.new(user=request.user) new_obj = True - request.session['cart_id'] = cart_obj.id + request.session["cart_id"] = cart_obj.id return cart_obj, new_obj def new(self, user=None): @@ -97,7 +97,9 @@ class TakenCourseManager(models.Manager): class TakenCourse(models.Model): student = models.ForeignKey(Student, on_delete=models.CASCADE) - course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='taken_courses') + course = models.ForeignKey( + Course, on_delete=models.CASCADE, related_name="taken_courses" + ) assignment = models.DecimalField(max_digits=5, decimal_places=2, default=0.0) mid_exam = models.DecimalField(max_digits=5, decimal_places=2, default=0.0) quiz = models.DecimalField(max_digits=5, decimal_places=2, default=0.0) @@ -109,14 +111,20 @@ class TakenCourse(models.Model): comment = models.CharField(choices=COMMENT, max_length=200, blank=True) def get_absolute_url(self): - return reverse('course_detail', kwargs={'slug': self.course.slug}) + return reverse("course_detail", kwargs={"slug": self.course.slug}) def __str__(self): return "{0} ({1})".format(self.course.title, self.course.code) # @staticmethod def get_total(self, assignment, mid_exam, quiz, attendance, final_exam): - return float(assignment) + float(mid_exam) + float(quiz) + float(attendance) + float(final_exam) + return ( + float(assignment) + + float(mid_exam) + + float(quiz) + + float(attendance) + + float(final_exam) + ) # @staticmethod def get_grade(self, total): @@ -191,7 +199,11 @@ class TakenCourse(models.Model): def calculate_gpa(self, total_credit_in_semester): current_semester = Semester.objects.get(is_current_semester=True) - student = TakenCourse.objects.filter(student=self.student, course__level=self.student.level, course__semester=current_semester) + student = TakenCourse.objects.filter( + student=self.student, + course__level=self.student.level, + course__semester=current_semester, + ) p = 0 point = 0 for i in student: @@ -220,14 +232,16 @@ class TakenCourse(models.Model): point = 0 p += int(credit) * point try: - gpa = (p / total_credit_in_semester) + gpa = p / total_credit_in_semester return round(gpa, 2) except ZeroDivisionError: return 0 - + def calculate_cgpa(self): current_semester = Semester.objects.get(is_current_semester=True) - previousResult = Result.objects.filter(student__id=self.student.id, level__lt=self.student.level) + previousResult = Result.objects.filter( + student__id=self.student.id, level__lt=self.student.level + ) previousCGPA = 0 for i in previousResult: if i.cgpa is not None: @@ -237,18 +251,24 @@ class TakenCourse(models.Model): first_sem_gpa = 0.0 sec_sem_gpa = 0.0 try: - first_sem_result = Result.objects.get(student=self.student.id, semester=FIRST, level=self.student.level) + first_sem_result = Result.objects.get( + student=self.student.id, semester=FIRST, level=self.student.level + ) first_sem_gpa += first_sem_result.gpa except: first_sem_gpa = 0 try: - sec_sem_result = Result.objects.get(student=self.student.id, semester=SECOND, level=self.student.level) + sec_sem_result = Result.objects.get( + student=self.student.id, semester=SECOND, level=self.student.level + ) sec_sem_gpa += sec_sem_result.gpa except: sec_sem_gpa = 0 - taken_courses = TakenCourse.objects.filter(student=self.student, student__level=self.student.level) + taken_courses = TakenCourse.objects.filter( + student=self.student, student__level=self.student.level + ) TCC = 0 TCP = 0 for i in taken_courses: diff --git a/result/urls.py b/result/urls.py index 4659aed..5f292b3 100644 --- a/result/urls.py +++ b/result/urls.py @@ -1,17 +1,21 @@ from django.urls import path from .views import ( - add_score, add_score_for, grade_result, assessment_result, - course_registration_form, result_sheet_pdf_view + add_score, + add_score_for, + grade_result, + assessment_result, + course_registration_form, + result_sheet_pdf_view, ) urlpatterns = [ - path('manage-score/', add_score, name='add_score'), - path('manage-score//', add_score_for, name='add_score_for'), - - path('grade/', grade_result, name="grade_results"), - path('assessment/', assessment_result, name="ass_results"), - - path('result/print//', result_sheet_pdf_view, name='result_sheet_pdf_view'), - path('registration/form/', course_registration_form, name='course_registration_form'), + path("manage-score/", add_score, name="add_score"), + path("manage-score//", add_score_for, name="add_score_for"), + path("grade/", grade_result, name="grade_results"), + path("assessment/", assessment_result, name="ass_results"), + path("result/print//", result_sheet_pdf_view, name="result_sheet_pdf_view"), + path( + "registration/form/", course_registration_form, name="course_registration_form" + ), ] diff --git a/result/views.py b/result/views.py index a06dee9..4c92322 100644 --- a/result/views.py +++ b/result/views.py @@ -12,13 +12,21 @@ from course.models import Course from accounts.decorators import lecturer_required, student_required from .models import TakenCourse, Result -#pdf +# pdf from django.core.files.storage import FileSystemStorage from django.http import HttpResponse, JsonResponse -from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, Image, LongTable +from reportlab.platypus import ( + SimpleDocTemplate, + Paragraph, + Spacer, + Table, + TableStyle, + Image, + LongTable, +) from reportlab.lib.styles import getSampleStyleSheet, black, ParagraphStyle -from reportlab.lib.enums import TA_JUSTIFY,TA_LEFT,TA_CENTER,TA_RIGHT +from reportlab.lib.enums import TA_JUSTIFY, TA_LEFT, TA_CENTER, TA_RIGHT from reportlab.platypus.tables import Table from reportlab.lib.units import inch from reportlab.lib import colors @@ -26,27 +34,32 @@ from .models import * cm = 2.54 + # ######################################################## # Score Add & Add for # ######################################################## @login_required @lecturer_required def add_score(request): - """ + """ Shows a page where a lecturer will select a course allocated to him for score entry. - in a specific semester and session + in a specific semester and session """ current_session = Session.objects.get(is_current_session=True) - current_semester = get_object_or_404(Semester, is_current_semester=True, session=current_session) + current_semester = get_object_or_404( + Semester, is_current_semester=True, session=current_session + ) # semester = Course.objects.filter(allocated_course__lecturer__pk=request.user.id, semester=current_semester) - courses = Course.objects.filter(allocated_course__lecturer__pk=request.user.id).filter(semester=current_semester) + courses = Course.objects.filter( + allocated_course__lecturer__pk=request.user.id + ).filter(semester=current_semester) context = { "current_session": current_session, "current_semester": current_semester, "courses": courses, } - return render(request, 'result/add_score.html', context) + return render(request, "result/add_score.html", context) @login_required @@ -54,13 +67,16 @@ def add_score(request): def add_score_for(request, id): """ Shows a page where a lecturer will add score for students that are taking courses allocated to him - in a specific semester and session + in a specific semester and session """ current_session = Session.objects.get(is_current_session=True) - current_semester = get_object_or_404(Semester, is_current_semester=True, session=current_session) - if request.method == 'GET': - courses = Course.objects.filter(allocated_course__lecturer__pk=request.user.id).filter( - semester=current_semester) + current_semester = get_object_or_404( + Semester, is_current_semester=True, session=current_session + ) + if request.method == "GET": + courses = Course.objects.filter( + allocated_course__lecturer__pk=request.user.id + ).filter(semester=current_semester) course = Course.objects.get(pk=id) # myclass = Class.objects.get(lecturer__pk=request.user.id) # myclass = get_object_or_404(Class, lecturer__pk=request.user.id) @@ -68,8 +84,13 @@ def add_score_for(request, id): # students = TakenCourse.objects.filter(course__allocated_course__lecturer__pk=request.user.id).filter( # course__id=id).filter(student__allocated_student__lecturer__pk=request.user.id).filter( # course__semester=current_semester) - students = TakenCourse.objects.filter(course__allocated_course__lecturer__pk=request.user.id).filter( - course__id=id).filter(course__semester=current_semester) + students = ( + TakenCourse.objects.filter( + course__allocated_course__lecturer__pk=request.user.id + ) + .filter(course__id=id) + .filter(course__semester=current_semester) + ) context = { "title": "Submit Score | DjangoSMS", "courses": courses, @@ -79,29 +100,40 @@ def add_score_for(request, id): "current_session": current_session, "current_semester": current_semester, } - return render(request, 'result/add_score_for.html', context) + return render(request, "result/add_score_for.html", context) - if request.method == 'POST': + if request.method == "POST": ids = () data = request.POST.copy() - data.pop('csrfmiddlewaretoken', None) # remove csrf_token + data.pop("csrfmiddlewaretoken", None) # remove csrf_token for key in data.keys(): - ids = ids + (str(key),) # gather all the all students id (i.e the keys) in a tuple - for s in range(0, len(ids)): # iterate over the list of student ids gathered above + ids = ids + ( + str(key), + ) # gather all the all students id (i.e the keys) in a tuple + for s in range( + 0, len(ids) + ): # iterate over the list of student ids gathered above student = TakenCourse.objects.get(id=ids[s]) # print(student) # print(student.student) # print(student.student.department.id) - courses = Course.objects.filter(level=student.student.level).filter(program__pk=student.student.department.id).filter( - semester=current_semester) # all courses of a specific level in current semester + courses = ( + Course.objects.filter(level=student.student.level) + .filter(program__pk=student.student.department.id) + .filter(semester=current_semester) + ) # all courses of a specific level in current semester total_credit_in_semester = 0 for i in courses: if i == courses.count(): break else: total_credit_in_semester += int(i.credit) - score = data.getlist(ids[s]) # get list of score for current student in the loop - assignment = score[0] # subscript the list to get the fisrt value > ca score + score = data.getlist( + ids[s] + ) # get list of score for current student in the loop + assignment = score[ + 0 + ] # subscript the list to get the fisrt value > ca score mid_exam = score[1] # do the same for exam score quiz = score[2] attendance = score[3] @@ -113,7 +145,13 @@ def add_score_for(request, id): obj.attendance = attendance # set current student attendance score obj.final_exam = final_exam # set current student final_exam score - obj.total = obj.get_total(assignment=assignment, mid_exam=mid_exam, quiz=quiz, attendance=attendance, final_exam=final_exam) + obj.total = obj.get_total( + assignment=assignment, + mid_exam=mid_exam, + quiz=quiz, + attendance=attendance, + final_exam=final_exam, + ) obj.grade = obj.get_grade(total=obj.total) # obj.total = obj.get_total(assignment, mid_exam, quiz, attendance, final_exam) @@ -129,13 +167,23 @@ def add_score_for(request, id): cgpa = obj.calculate_cgpa() try: - a = Result.objects.get(student=student.student, semester=current_semester, session=current_session, level=student.student.level) + a = Result.objects.get( + student=student.student, + semester=current_semester, + session=current_session, + level=student.student.level, + ) a.gpa = gpa a.cgpa = cgpa a.save() except: - Result.objects.get_or_create(student=student.student, gpa=gpa, semester=current_semester, - session=current_session, level=student.student.level) + Result.objects.get_or_create( + student=student.student, + gpa=gpa, + semester=current_semester, + session=current_session, + level=student.student.level, + ) # try: # a = Result.objects.get(student=student.student, semester=current_semester, level=student.student.level) @@ -145,9 +193,11 @@ def add_score_for(request, id): # except: # Result.objects.get_or_create(student=student.student, gpa=gpa, semester=current_semester, level=student.student.level) - messages.success(request, 'Successfully Recorded! ') - return HttpResponseRedirect(reverse_lazy('add_score_for', kwargs={'id': id})) - return HttpResponseRedirect(reverse_lazy('add_score_for', kwargs={'id': id})) + messages.success(request, "Successfully Recorded! ") + return HttpResponseRedirect(reverse_lazy("add_score_for", kwargs={"id": id})) + return HttpResponseRedirect(reverse_lazy("add_score_for", kwargs={"id": id})) + + # ######################################################## @@ -155,7 +205,9 @@ def add_score_for(request, id): @student_required def grade_result(request): student = Student.objects.get(student__pk=request.user.id) - courses = TakenCourse.objects.filter(student__student__pk=request.user.id).filter(course__level=student.level) + courses = TakenCourse.objects.filter(student__student__pk=request.user.id).filter( + course__level=student.level + ) # total_credit_in_semester = 0 results = Result.objects.filter(student__student__pk=request.user.id) @@ -180,7 +232,11 @@ def grade_result(request): for i in results: previousLEVEL = i.level try: - a = Result.objects.get(student__student__pk=request.user.id, level=previousLEVEL, semester="Second") + a = Result.objects.get( + student__student__pk=request.user.id, + level=previousLEVEL, + semester="Second", + ) previousCGPA = a.cgpa break except: @@ -191,20 +247,23 @@ def grade_result(request): "results": results, "sorted_result": sorted_result, "student": student, - 'total_first_semester_credit': total_first_semester_credit, - 'total_sec_semester_credit': total_sec_semester_credit, - 'total_first_and_second_semester_credit': total_first_semester_credit + total_sec_semester_credit, + "total_first_semester_credit": total_first_semester_credit, + "total_sec_semester_credit": total_sec_semester_credit, + "total_first_and_second_semester_credit": total_first_semester_credit + + total_sec_semester_credit, "previousCGPA": previousCGPA, } - return render(request, 'result/grade_results.html', context) + return render(request, "result/grade_results.html", context) @login_required @student_required def assessment_result(request): student = Student.objects.get(student__pk=request.user.id) - courses = TakenCourse.objects.filter(student__student__pk=request.user.id, course__level=student.level) + courses = TakenCourse.objects.filter( + student__student__pk=request.user.id, course__level=student.level + ) result = Result.objects.filter(student__student__pk=request.user.id) context = { @@ -212,8 +271,8 @@ def assessment_result(request): "result": result, "student": student, } - - return render(request, 'result/assessment_results.html', context) + + return render(request, "result/assessment_results.html", context) @login_required @@ -225,14 +284,29 @@ def result_sheet_pdf_view(request, id): course = get_object_or_404(Course, id=id) no_of_pass = TakenCourse.objects.filter(course__pk=id, comment="PASS").count() no_of_fail = TakenCourse.objects.filter(course__pk=id, comment="FAIL").count() - fname = str(current_semester) + '_semester_' + str(current_session) + '_' + str(course) + '_resultSheet.pdf' + fname = ( + str(current_semester) + + "_semester_" + + str(current_session) + + "_" + + str(course) + + "_resultSheet.pdf" + ) fname = fname.replace("/", "-") flocation = settings.MEDIA_ROOT + "/result_sheet/" + fname - doc = SimpleDocTemplate(flocation, rightMargin=0, leftMargin=6.5 * cm, topMargin=0.3 * cm, bottomMargin=0) + doc = SimpleDocTemplate( + flocation, + rightMargin=0, + leftMargin=6.5 * cm, + topMargin=0.3 * cm, + bottomMargin=0, + ) styles = getSampleStyleSheet() - styles.add(ParagraphStyle( name="ParagraphTitle", fontSize=11, fontName="FreeSansBold")) - Story = [Spacer(1,.2)] + styles.add( + ParagraphStyle(name="ParagraphTitle", fontSize=11, fontName="FreeSansBold") + ) + Story = [Spacer(1, 0.2)] style = styles["Normal"] # picture = request.user.picture @@ -250,21 +324,27 @@ def result_sheet_pdf_view(request, id): print("\nsettings.MEDIA_ROOT", settings.MEDIA_ROOT) print("\nsettings.STATICFILES_DIRS[0]", settings.STATICFILES_DIRS[0]) logo = settings.STATICFILES_DIRS[0] + "/img/logo.png" - im = Image(logo, 1*inch, 1*inch) + im = Image(logo, 1 * inch, 1 * inch) im.__setattr__("_offs_x", -200) im.__setattr__("_offs_y", -45) Story.append(im) - + style = getSampleStyleSheet() normal = style["Normal"] normal.alignment = TA_CENTER normal.fontName = "Helvetica" normal.fontSize = 12 normal.leading = 15 - title = " "+str(current_semester) + " Semester " + str(current_session) + " Result Sheet" + title = ( + " " + + str(current_semester) + + " Semester " + + str(current_session) + + " Result Sheet" + ) title = Paragraph(title.upper(), normal) Story.append(title) - Story.append(Spacer(1,0.1*inch)) + Story.append(Spacer(1, 0.1 * inch)) style = getSampleStyleSheet() normal = style["Normal"] @@ -275,7 +355,7 @@ def result_sheet_pdf_view(request, id): title = "Course lecturer: " + request.user.get_full_name + "" title = Paragraph(title.upper(), normal) Story.append(title) - Story.append(Spacer(1,0.1*inch)) + Story.append(Spacer(1, 0.1 * inch)) normal = style["Normal"] normal.alignment = TA_CENTER @@ -286,46 +366,74 @@ def result_sheet_pdf_view(request, id): title = "Level: " + str(level.course.level) title = Paragraph(title.upper(), normal) Story.append(title) - Story.append(Spacer(1,.6*inch)) - + Story.append(Spacer(1, 0.6 * inch)) + elements = [] count = 0 - header = [('S/N', 'ID NO.', 'FULL NAME', 'TOTAL', 'GRADE', 'POINT', 'COMMENT')] + header = [("S/N", "ID NO.", "FULL NAME", "TOTAL", "GRADE", "POINT", "COMMENT")] - table_header = Table(header, [inch], [0.5*inch]) + table_header = Table(header, [inch], [0.5 * inch]) table_header.setStyle( - TableStyle([ - ('BACKGROUND',(0,0),(-1,-1),colors.black), - ('TEXTCOLOR',(1,0),(-1,-1),colors.white), - ('TEXTCOLOR',(0,0),(0,0),colors.cyan), - ('ALIGN',(0,0),(-1,-1),'CENTER'), - ('VALIGN',(0,0),(-1,-1),'MIDDLE'), - ('BOX',(0,0),(-1,-1),1,colors.black), - ])) + TableStyle( + [ + ("BACKGROUND", (0, 0), (-1, -1), colors.black), + ("TEXTCOLOR", (1, 0), (-1, -1), colors.white), + ("TEXTCOLOR", (0, 0), (0, 0), colors.cyan), + ("ALIGN", (0, 0), (-1, -1), "CENTER"), + ("VALIGN", (0, 0), (-1, -1), "MIDDLE"), + ("BOX", (0, 0), (-1, -1), 1, colors.black), + ] + ) + ) Story.append(table_header) for student in result: - - data = [(count+1, student.student.student.username.upper(), Paragraph(student.student.student.get_full_name.capitalize(), styles['Normal']), - student.total, student.grade, student.point, student.comment)] + data = [ + ( + count + 1, + student.student.student.username.upper(), + Paragraph( + student.student.student.get_full_name.capitalize(), styles["Normal"] + ), + student.total, + student.grade, + student.point, + student.comment, + ) + ] color = colors.black - if student.grade == 'F': + if student.grade == "F": color = colors.red count += 1 t_body = Table(data, colWidths=[inch]) t_body.setStyle( - TableStyle([ - ('INNERGRID', (0,0), (-1,-1), 0.05, colors.black), - ('BOX', (0,0), (-1,-1), 0.1, colors.black), - ])) + TableStyle( + [ + ("INNERGRID", (0, 0), (-1, -1), 0.05, colors.black), + ("BOX", (0, 0), (-1, -1), 0.1, colors.black), + ] + ) + ) Story.append(t_body) - Story.append(Spacer(1,1*inch)) - style_right = ParagraphStyle(name='right', parent=styles['Normal'], alignment=TA_RIGHT) + Story.append(Spacer(1, 1 * inch)) + style_right = ParagraphStyle( + name="right", parent=styles["Normal"], alignment=TA_RIGHT + ) tbl_data = [ - [Paragraph("Date:_____________________________", styles["Normal"]), Paragraph("No. of PASS: " + str(no_of_pass), style_right)], - [Paragraph("Siganture / Stamp: _____________________________", styles["Normal"]), Paragraph("No. of FAIL: " + str(no_of_fail), style_right)]] + [ + Paragraph("Date:_____________________________", styles["Normal"]), + Paragraph("No. of PASS: " + str(no_of_pass), style_right), + ], + [ + Paragraph( + "Siganture / Stamp: _____________________________", + styles["Normal"], + ), + Paragraph("No. of FAIL: " + str(no_of_fail), style_right), + ], + ] tbl = Table(tbl_data) Story.append(tbl) @@ -333,8 +441,8 @@ def result_sheet_pdf_view(request, id): fs = FileSystemStorage(settings.MEDIA_ROOT + "/result_sheet") with fs.open(fname) as pdf: - response = HttpResponse(pdf, content_type='application/pdf') - response['Content-Disposition'] = 'inline; filename=' + fname + '' + response = HttpResponse(pdf, content_type="application/pdf") + response["Content-Disposition"] = "inline; filename=" + fname + "" return response return response @@ -345,16 +453,18 @@ def course_registration_form(request): current_semester = Semester.objects.get(is_current_semester=True) current_session = Session.objects.get(is_current_session=True) courses = TakenCourse.objects.filter(student__student__id=request.user.id) - fname = request.user.username + '.pdf' + fname = request.user.username + ".pdf" fname = fname.replace("/", "-") # flocation = '/tmp/' + fname # print(MEDIA_ROOT + "\\" + fname) flocation = settings.MEDIA_ROOT + "/registration_form/" + fname - doc = SimpleDocTemplate(flocation, rightMargin=15, leftMargin=15, topMargin=0, bottomMargin=0) + doc = SimpleDocTemplate( + flocation, rightMargin=15, leftMargin=15, topMargin=0, bottomMargin=0 + ) styles = getSampleStyleSheet() - Story = [Spacer(1,0.5)] - Story.append(Spacer(1,0.4*inch)) + Story = [Spacer(1, 0.5)] + Story.append(Spacer(1, 0.4 * inch)) style = styles["Normal"] style = getSampleStyleSheet() @@ -363,11 +473,11 @@ def course_registration_form(request): normal.fontName = "Helvetica" normal.fontSize = 12 normal.leading = 18 - title = "EZOD UNIVERSITY OF TECHNOLOGY, ADAMA" + title = "EZOD UNIVERSITY OF TECHNOLOGY, ADAMA" title = Paragraph(title.upper(), normal) Story.append(title) style = getSampleStyleSheet() - + school = style["Normal"] school.alignment = TA_CENTER school.fontName = "Helvetica" @@ -378,7 +488,7 @@ def course_registration_form(request): Story.append(school_title) style = getSampleStyleSheet() - Story.append(Spacer(1,0.1*inch)) + Story.append(Spacer(1, 0.1 * inch)) department = style["Normal"] department.alignment = TA_CENTER department.fontName = "Helvetica" @@ -387,22 +497,38 @@ def course_registration_form(request): department_title = "DEPARTMENT OF COMPUTER SCIENCE & ENGINEERING" department_title = Paragraph(department_title, department) Story.append(department_title) - Story.append(Spacer(1,.3*inch)) - + Story.append(Spacer(1, 0.3 * inch)) + title = "STUDENT COURSE REGISTRATION FORM" title = Paragraph(title.upper(), normal) Story.append(title) student = Student.objects.get(student__pk=request.user.id) - style_right = ParagraphStyle(name='right', parent=styles['Normal']) + style_right = ParagraphStyle(name="right", parent=styles["Normal"]) tbl_data = [ - [Paragraph("Registration Number : " + request.user.username.upper() + "", styles["Normal"])], - [Paragraph("Name : " + request.user.get_full_name.upper() + "", styles["Normal"])], - [Paragraph("Session : " + current_session.session.upper() + "", styles["Normal"]), Paragraph("Level: " + student.level + "", styles["Normal"]) - ]] + [ + Paragraph( + "Registration Number : " + request.user.username.upper() + "", + styles["Normal"], + ) + ], + [ + Paragraph( + "Name : " + request.user.get_full_name.upper() + "", + styles["Normal"], + ) + ], + [ + Paragraph( + "Session : " + current_session.session.upper() + "", + styles["Normal"], + ), + Paragraph("Level: " + student.level + "", styles["Normal"]), + ], + ] tbl = Table(tbl_data) Story.append(tbl) - Story.append(Spacer(1, 0.6*inch)) + Story.append(Spacer(1, 0.6 * inch)) style = getSampleStyleSheet() semester = style["Normal"] @@ -418,44 +544,66 @@ def course_registration_form(request): # FIRST SEMESTER count = 0 - header = [('S/No', 'Course Code', 'Course Title', 'Unit', Paragraph('Name, Siganture of course lecturer & Date', style['Normal']))] - table_header = Table(header,1*[1.4*inch], 1*[0.5*inch]) + header = [ + ( + "S/No", + "Course Code", + "Course Title", + "Unit", + Paragraph("Name, Siganture of course lecturer & Date", style["Normal"]), + ) + ] + table_header = Table(header, 1 * [1.4 * inch], 1 * [0.5 * inch]) table_header.setStyle( - TableStyle([ - ('ALIGN',(-2,-2), (-2,-2),'CENTER'), - ('VALIGN',(-2,-2), (-2,-2),'MIDDLE'), - ('ALIGN',(1,0), (1,0),'CENTER'), - ('VALIGN',(1,0), (1,0),'MIDDLE'), - ('ALIGN',(0,0), (0,0),'CENTER'), - ('VALIGN',(0,0), (0,0),'MIDDLE'), - ('ALIGN',(-4,0), (-4,0),'LEFT'), - ('VALIGN',(-4,0), (-4,0),'MIDDLE'), - ('ALIGN',(-3,0), (-3,0),'LEFT'), - ('VALIGN',(-3,0), (-3,0),'MIDDLE'), - ('TEXTCOLOR',(0,-1),(-1,-1),colors.black), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) + TableStyle( + [ + ("ALIGN", (-2, -2), (-2, -2), "CENTER"), + ("VALIGN", (-2, -2), (-2, -2), "MIDDLE"), + ("ALIGN", (1, 0), (1, 0), "CENTER"), + ("VALIGN", (1, 0), (1, 0), "MIDDLE"), + ("ALIGN", (0, 0), (0, 0), "CENTER"), + ("VALIGN", (0, 0), (0, 0), "MIDDLE"), + ("ALIGN", (-4, 0), (-4, 0), "LEFT"), + ("VALIGN", (-4, 0), (-4, 0), "MIDDLE"), + ("ALIGN", (-3, 0), (-3, 0), "LEFT"), + ("VALIGN", (-3, 0), (-3, 0), "MIDDLE"), + ("TEXTCOLOR", (0, -1), (-1, -1), colors.black), + ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), + ("BOX", (0, 0), (-1, -1), 0.25, colors.black), + ] + ) + ) Story.append(table_header) first_semester_unit = 0 for course in courses: if course.course.semester == FIRST: first_semester_unit += int(course.course.credit) - data = [(count+1, course.course.code.upper(), Paragraph(course.course.title, style['Normal']), course.course.credit, '')] + data = [ + ( + count + 1, + course.course.code.upper(), + Paragraph(course.course.title, style["Normal"]), + course.course.credit, + "", + ) + ] color = colors.black count += 1 - table_body=Table(data,1*[1.4*inch], 1*[0.3*inch]) + table_body = Table(data, 1 * [1.4 * inch], 1 * [0.3 * inch]) table_body.setStyle( - TableStyle([ - ('ALIGN',(-2,-2), (-2,-2),'CENTER'), - ('ALIGN',(1,0), (1,0),'CENTER'), - ('ALIGN',(0,0), (0,0),'CENTER'), - ('ALIGN',(-4,0), (-4,0),'LEFT'), - ('TEXTCOLOR',(0,-1),(-1,-1),colors.black), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) + TableStyle( + [ + ("ALIGN", (-2, -2), (-2, -2), "CENTER"), + ("ALIGN", (1, 0), (1, 0), "CENTER"), + ("ALIGN", (0, 0), (0, 0), "CENTER"), + ("ALIGN", (-4, 0), (-4, 0), "LEFT"), + ("TEXTCOLOR", (0, -1), (-1, -1), colors.black), + ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), + ("BOX", (0, 0), (-1, -1), 0.25, colors.black), + ] + ) + ) Story.append(table_body) style = getSampleStyleSheet() @@ -464,12 +612,14 @@ def course_registration_form(request): semester.fontName = "Helvetica" semester.fontSize = 8 semester.leading = 18 - semester_title = "Total Second First Credit : " + str(first_semester_unit) + "" + semester_title = ( + "Total Second First Credit : " + str(first_semester_unit) + "" + ) semester_title = Paragraph(semester_title, semester) Story.append(semester_title) # FIRST SEMESTER ENDS HERE - Story.append(Spacer(1, 0.6*inch)) + Story.append(Spacer(1, 0.6 * inch)) style = getSampleStyleSheet() semester = style["Normal"] @@ -482,44 +632,68 @@ def course_registration_form(request): Story.append(semester_title) # SECOND SEMESTER count = 0 - header = [('S/No', 'Course Code', 'Course Title', 'Unit', Paragraph('Name, Signature of course lecturer & Date', style['Normal']))] - table_header = Table(header,1*[1.4*inch], 1*[0.5*inch]) + header = [ + ( + "S/No", + "Course Code", + "Course Title", + "Unit", + Paragraph( + "Name, Signature of course lecturer & Date", style["Normal"] + ), + ) + ] + table_header = Table(header, 1 * [1.4 * inch], 1 * [0.5 * inch]) table_header.setStyle( - TableStyle([ - ('ALIGN',(-2,-2), (-2,-2),'CENTER'), - ('VALIGN',(-2,-2), (-2,-2),'MIDDLE'), - ('ALIGN',(1,0), (1,0),'CENTER'), - ('VALIGN',(1,0), (1,0),'MIDDLE'), - ('ALIGN',(0,0), (0,0),'CENTER'), - ('VALIGN',(0,0), (0,0),'MIDDLE'), - ('ALIGN',(-4,0), (-4,0),'LEFT'), - ('VALIGN',(-4,0), (-4,0),'MIDDLE'), - ('ALIGN',(-3,0), (-3,0),'LEFT'), - ('VALIGN',(-3,0), (-3,0),'MIDDLE'), - ('TEXTCOLOR',(0,-1),(-1,-1),colors.black), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) + TableStyle( + [ + ("ALIGN", (-2, -2), (-2, -2), "CENTER"), + ("VALIGN", (-2, -2), (-2, -2), "MIDDLE"), + ("ALIGN", (1, 0), (1, 0), "CENTER"), + ("VALIGN", (1, 0), (1, 0), "MIDDLE"), + ("ALIGN", (0, 0), (0, 0), "CENTER"), + ("VALIGN", (0, 0), (0, 0), "MIDDLE"), + ("ALIGN", (-4, 0), (-4, 0), "LEFT"), + ("VALIGN", (-4, 0), (-4, 0), "MIDDLE"), + ("ALIGN", (-3, 0), (-3, 0), "LEFT"), + ("VALIGN", (-3, 0), (-3, 0), "MIDDLE"), + ("TEXTCOLOR", (0, -1), (-1, -1), colors.black), + ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), + ("BOX", (0, 0), (-1, -1), 0.25, colors.black), + ] + ) + ) Story.append(table_header) second_semester_unit = 0 for course in courses: if course.course.semester == SECOND: second_semester_unit += int(course.course.credit) - data = [(count+1, course.course.code.upper(), Paragraph(course.course.title, style['Normal']), course.course.credit, '')] + data = [ + ( + count + 1, + course.course.code.upper(), + Paragraph(course.course.title, style["Normal"]), + course.course.credit, + "", + ) + ] color = colors.black count += 1 - table_body=Table(data,1*[1.4*inch], 1*[0.3*inch]) + table_body = Table(data, 1 * [1.4 * inch], 1 * [0.3 * inch]) table_body.setStyle( - TableStyle([ - ('ALIGN',(-2,-2), (-2,-2),'CENTER'), - ('ALIGN',(1,0), (1,0),'CENTER'), - ('ALIGN',(0,0), (0,0),'CENTER'), - ('ALIGN',(-4,0), (-4,0),'LEFT'), - ('TEXTCOLOR',(0,-1),(-1,-1),colors.black), - ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black), - ('BOX', (0,0), (-1,-1), 0.25, colors.black), - ])) + TableStyle( + [ + ("ALIGN", (-2, -2), (-2, -2), "CENTER"), + ("ALIGN", (1, 0), (1, 0), "CENTER"), + ("ALIGN", (0, 0), (0, 0), "CENTER"), + ("ALIGN", (-4, 0), (-4, 0), "LEFT"), + ("TEXTCOLOR", (0, -1), (-1, -1), colors.black), + ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), + ("BOX", (0, 0), (-1, -1), 0.25, colors.black), + ] + ) + ) Story.append(table_body) style = getSampleStyleSheet() @@ -528,7 +702,9 @@ def course_registration_form(request): semester.fontName = "Helvetica" semester.fontSize = 8 semester.leading = 18 - semester_title = "Total Second Semester Credit : " + str(second_semester_unit) + "" + semester_title = ( + "Total Second Semester Credit : " + str(second_semester_unit) + "" + ) semester_title = Paragraph(semester_title, semester) Story.append(semester_title) @@ -540,22 +716,28 @@ def course_registration_form(request): certification.fontSize = 8 certification.leading = 18 student = Student.objects.get(student__pk=request.user.id) - certification_text = "CERTIFICATION OF REGISTRATION: I certify that " + str(request.user.get_full_name.upper()) + "\ - has been duly registered for the " + student.level + " level of study in the department\ + certification_text = ( + "CERTIFICATION OF REGISTRATION: I certify that " + + str(request.user.get_full_name.upper()) + + "\ + has been duly registered for the " + + student.level + + " level of study in the department\ of COMPUTER SICENCE & ENGINEERING and that the courses and credits registered are as approved by the senate of the University" + ) certification_text = Paragraph(certification_text, certification) Story.append(certification_text) # FIRST SEMESTER ENDS HERE logo = settings.STATICFILES_DIRS[0] + "/img/logo.png" - im_logo = Image(logo, 1*inch, 1*inch) + im_logo = Image(logo, 1 * inch, 1 * inch) im_logo.__setattr__("_offs_x", -218) im_logo.__setattr__("_offs_y", 480) Story.append(im_logo) - picture = settings.BASE_DIR + request.user.get_picture() - im = Image(picture, 1.0*inch, 1.0*inch) + picture = settings.BASE_DIR + request.user.get_picture() + im = Image(picture, 1.0 * inch, 1.0 * inch) im.__setattr__("_offs_x", 218) im.__setattr__("_offs_y", 550) Story.append(im) @@ -563,7 +745,7 @@ def course_registration_form(request): doc.build(Story) fs = FileSystemStorage(settings.MEDIA_ROOT + "/registration_form") with fs.open(fname) as pdf: - response = HttpResponse(pdf, content_type='application/pdf') - response['Content-Disposition'] = 'inline; filename='+fname+'' + response = HttpResponse(pdf, content_type="application/pdf") + response["Content-Disposition"] = "inline; filename=" + fname + "" return response return response diff --git a/search/apps.py b/search/apps.py index 5726231..f540091 100644 --- a/search/apps.py +++ b/search/apps.py @@ -2,4 +2,4 @@ from django.apps import AppConfig class SearchConfig(AppConfig): - name = 'search' + name = "search" diff --git a/search/urls.py b/search/urls.py index 0a05833..2896985 100644 --- a/search/urls.py +++ b/search/urls.py @@ -2,5 +2,5 @@ from django.urls import path from .views import SearchView urlpatterns = [ - path('', SearchView.as_view(), name='query'), + path("", SearchView.as_view(), name="query"), ] diff --git a/search/views.py b/search/views.py index d8e1e88..0321db1 100644 --- a/search/views.py +++ b/search/views.py @@ -27,14 +27,6 @@ # ''' - - - - - - - - # search.views.py from itertools import chain from django.views.generic import ListView @@ -48,36 +40,31 @@ from quiz.models import Quiz class SearchView(ListView): - template_name = 'search/search_view.html' + template_name = "search/search_view.html" paginate_by = 20 count = 0 - + def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) - context['count'] = self.count or 0 - context['query'] = self.request.GET.get('q') + context["count"] = self.count or 0 + context["query"] = self.request.GET.get("q") return context def get_queryset(self): request = self.request - query = request.GET.get('q', None) - + query = request.GET.get("q", None) + if query is not None: - news_events_results = NewsAndEvents.objects.search(query) - program_results = Program.objects.search(query) - course_results = Course.objects.search(query) - quiz_results = Quiz.objects.search(query) - - # combine querysets + news_events_results = NewsAndEvents.objects.search(query) + program_results = Program.objects.search(query) + course_results = Course.objects.search(query) + quiz_results = Quiz.objects.search(query) + + # combine querysets queryset_chain = chain( - news_events_results, - program_results, - course_results, - quiz_results - ) - qs = sorted(queryset_chain, - key=lambda instance: instance.pk, - reverse=True) - self.count = len(qs) # since qs is actually a list + news_events_results, program_results, course_results, quiz_results + ) + qs = sorted(queryset_chain, key=lambda instance: instance.pk, reverse=True) + self.count = len(qs) # since qs is actually a list return qs - return NewsAndEvents.objects.none() # just an empty queryset as default + return NewsAndEvents.objects.none() # just an empty queryset as default