Skip to content

Commit

Permalink
feat: integrated the score view for the students and student login (o…
Browse files Browse the repository at this point in the history
…cadotechnology#150)

* feat: integrated the scoreview for the students and student login

* resolve merge conflict

* implemented rtk with the fetching of score data

* uncomment the regex path

* added indy forms

* final touch ups

* merge

* added a popup for delete account

* updating messages

* moved messages from backend to frontend

* remove json.stringify from JSX

* remove unwanted handleClose

* updated the joinrequeststudent

* added a revoke option

* revert YourAccount

* remove unused method

* uncommented settings csrf

* merge commit

* merge conflicts and reverting settings.json

* added requests and revokes endpoints to the app

* fix indentation

* adding new line

* added a fix to the messags on the student page

* fixes due to comments #1

* update status code variable

* push latest changes

* fix error errors

* add errors variable above

* fix static access_code

* merge conflict

Co-Authored-By: KamilPawel <kamilpawel@Kamil>
  • Loading branch information
KamilPawel and KamilPawel authored Sep 15, 2023
1 parent df0dbcb commit 2bf397a
Show file tree
Hide file tree
Showing 28 changed files with 1,804 additions and 505 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
}
168 changes: 126 additions & 42 deletions backend/portal/forms/play.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@

class StudentClassCodeForm(forms.Form):
access_code = forms.CharField(
widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "Class code"}),
widget=forms.TextInput(
attrs={"autocomplete": "off", "placeholder": "Class code"}
),
help_text="Enter your class code",
)

Expand All @@ -27,18 +29,24 @@ def clean(self):

if access_code:
if re.fullmatch(ACCESS_CODE_PATTERN, access_code.upper()) is None:
raise forms.ValidationError("Uh oh! You didn't input a valid class code.")
raise forms.ValidationError(
"Uh oh! You didn't input a valid class code."
)

return self.cleaned_data


class StudentLoginForm(AuthenticationForm):
username = forms.CharField(
widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "Username"}),
widget=forms.TextInput(
attrs={"autocomplete": "off", "placeholder": "Username"}
),
help_text="Enter your username",
)
password = forms.CharField(
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "Password"}
),
help_text="Enter your password",
)

Expand All @@ -51,7 +59,9 @@ def clean(self):
password = self.cleaned_data.get("password", None)

if name and self.access_code and password:
student, user = self.check_for_errors(name, self.access_code, password)
student, user = self.check_for_errors(
name, self.access_code, password
)

self.student = student
self.user_cache = user
Expand All @@ -60,43 +70,63 @@ def clean(self):
def check_for_errors(self, name, access_code, password):
classes = Class.objects.filter(access_code__iexact=access_code)
if len(classes) != 1:
raise forms.ValidationError("Invalid name, class access code or password")
raise forms.ValidationError(
"Invalid name, class access code or password"
)
klass = classes[0]

name = stripStudentName(name)

students = Student.objects.filter(new_user__first_name__iexact=name, class_field=klass)
students = Student.objects.filter(
new_user__first_name__iexact=name, class_field=klass
)
if len(students) != 1:
raise forms.ValidationError("Invalid name, class access code or password")
raise forms.ValidationError(
"Invalid name, class access code or password"
)

student = students[0]
user = authenticate(username=student.new_user.username, password=password.lower())
user = authenticate(
username=student.new_user.username, password=password.lower()
)

# Try the case sensitive password too, for previous accounts that don't have the lowercase one stored
if user is None:
user = authenticate(username=student.new_user.username, password=password)
user = authenticate(
username=student.new_user.username, password=password
)

if user is None:
raise forms.ValidationError("Invalid name, class access code or password")
raise forms.ValidationError(
"Invalid name, class access code or password"
)
if not user.is_active:
raise forms.ValidationError("This user account has been deactivated")
raise forms.ValidationError(
"This user account has been deactivated"
)

return student, user


class StudentEditAccountForm(forms.Form):
password = forms.CharField(
new_password = forms.CharField(
required=True,
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "New password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "New password"}
),
help_text="Enter new password",
)
confirm_password = forms.CharField(
repeat_password = forms.CharField(
required=True,
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Confirm new password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "Confirm new password"}
),
help_text="Confirm new password",
)
current_password = forms.CharField(
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Current password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "Current password"}
),
help_text="Enter your current password",
)

Expand All @@ -115,28 +145,44 @@ class IndependentStudentEditAccountForm(forms.Form):
name = forms.CharField(
max_length=100,
required=False,
widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "Name"}),
widget=forms.TextInput(
attrs={"autocomplete": "off", "placeholder": "Name"}
),
help_text="Enter your name",
)
email = forms.EmailField(
required=False,
widget=forms.EmailInput(attrs={"autocomplete": "off", "placeholder": "New email address (optional)"}),
widget=forms.EmailInput(
attrs={
"autocomplete": "off",
"placeholder": "New email address (optional)",
}
),
help_text="Enter new email address (optional)",
)
password = forms.CharField(
new_password = forms.CharField(
required=False,
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "New password (optional)"}),
widget=forms.PasswordInput(
attrs={
"autocomplete": "off",
"placeholder": "New password (optional)",
}
),
help_text="Enter new password (optional)",
)
confirm_password = forms.CharField(
label="Confirm new password",
required=False,
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Confirm new password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "Confirm new password"}
),
help_text="Confirm new password",
)
current_password = forms.CharField(
label="Current password",
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Current password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "Current password"}
),
help_text="Enter your current password",
)

Expand All @@ -151,12 +197,16 @@ def clean_name(self):
raise forms.ValidationError("This field is required")

if re.match(re.compile("^[\w ]+$"), name) is None:
raise forms.ValidationError("Names may only contain letters, numbers, dashes, underscores, and spaces.")
raise forms.ValidationError(
"Names may only contain letters, numbers, dashes, underscores, and spaces."
)

return name

def clean_password(self):
return form_clean_password(self, "password", PasswordStrength.INDEPENDENT)
return form_clean_password(
self, "password", PasswordStrength.INDEPENDENT
)

def clean(self):
return clean_confirm_password(self, independent=True)
Expand All @@ -179,7 +229,9 @@ def clean_confirm_password(self, independent=True):

if current_password and not self.user.check_password(current_password):
# If it's not an independent student, check their lowercase password as well
if independent or not self.user.check_password(current_password.lower()):
if independent or not self.user.check_password(
current_password.lower()
):
raise forms.ValidationError("Your current password was incorrect")

return self.cleaned_data
Expand All @@ -193,46 +245,63 @@ class IndependentStudentSignupForm(forms.Form):
date_of_birth = forms.DateTimeField(
help_text="Please enter your date of birth (we do not store this information).",
widget=forms.SelectDateWidget(
years=range(date.today().year, date.today().year - 100, -1), empty_label=("Year", "Month", "Day")
years=range(date.today().year, date.today().year - 100, -1),
empty_label=("Year", "Month", "Day"),
),
required=False,
)

name = forms.CharField(
max_length=100,
help_text="Enter full name",
widget=forms.TextInput(attrs={"autocomplete": "off", "placeholder": "Full name"}),
widget=forms.TextInput(
attrs={"autocomplete": "off", "placeholder": "Full name"}
),
)

email = forms.EmailField(
help_text="Enter your email address",
widget=forms.EmailInput(attrs={"autocomplete": "off", "placeholder": "Email address"}),
widget=forms.EmailInput(
attrs={"autocomplete": "off", "placeholder": "Email address"}
),
)

consent_ticked = forms.BooleanField(widget=forms.CheckboxInput(), initial=False, required=True)
newsletter_ticked = forms.BooleanField(widget=forms.CheckboxInput(), initial=False, required=False)
consent_ticked = forms.BooleanField(
widget=forms.CheckboxInput(), initial=False, required=True
)
newsletter_ticked = forms.BooleanField(
widget=forms.CheckboxInput(), initial=False, required=False
)

password = forms.CharField(
help_text="Enter a password",
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "Password"}
),
)

confirm_password = forms.CharField(
help_text="Repeat password",
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Repeat password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "Repeat password"}
),
)

# captcha = ReCaptchaField(widget=ReCaptchaV2Invisible)

def clean_name(self):
name = self.cleaned_data.get("name", None)
if re.match(re.compile("^[\w ]+$"), name) is None:
raise forms.ValidationError("Names may only contain letters, numbers, dashes, underscores, and spaces.")
raise forms.ValidationError(
"Names may only contain letters, numbers, dashes, underscores, and spaces."
)

return name

def clean_password(self):
return form_clean_password(self, "password", PasswordStrength.INDEPENDENT)
return form_clean_password(
self, "password", PasswordStrength.INDEPENDENT
)

def clean(self):
password = self.cleaned_data.get("password", None)
Expand All @@ -246,11 +315,15 @@ def clean(self):

class IndependentStudentLoginForm(AuthenticationForm):
username = forms.EmailField(
widget=forms.EmailInput(attrs={"autocomplete": "off", "placeholder": "Email address"}),
widget=forms.EmailInput(
attrs={"autocomplete": "off", "placeholder": "Email address"}
),
help_text="Enter your email address",
)
password = forms.CharField(
widget=forms.PasswordInput(attrs={"autocomplete": "off", "placeholder": "Password"}),
widget=forms.PasswordInput(
attrs={"autocomplete": "off", "placeholder": "Password"}
),
help_text="Enter your password",
)

Expand All @@ -273,19 +346,30 @@ def show_invalid_login_message(self):


class StudentJoinOrganisationForm(forms.Form):
access_code = forms.CharField(label="Class Access Code", widget=forms.TextInput(attrs={"placeholder": "AB123"}))
access_code = forms.CharField(
label="Class Access Code",
widget=forms.TextInput(attrs={"placeholder": "AB123"}),
)

def clean(self):
access_code = self.cleaned_data.get("access_code", None)

if access_code:
classes = Class.objects.filter(access_code=access_code)
if len(classes) != 1:
raise forms.ValidationError("Cannot find the school or club and/or class")
raise forms.ValidationError(
"Cannot find the school or club and/or class"
)
self.klass = classes[0]
if not self.klass.always_accept_requests:
if self.klass.accept_requests_until is None:
raise forms.ValidationError("Cannot find the school or club and/or class")
elif (self.klass.accept_requests_until - timezone.now()) < timedelta():
raise forms.ValidationError("Cannot find the school or club and/or class")
raise forms.ValidationError(
"Cannot find the school or club and/or class"
)
elif (
self.klass.accept_requests_until - timezone.now()
) < timedelta():
raise forms.ValidationError(
"Cannot find the school or club and/or class"
)
return self.cleaned_data
4 changes: 2 additions & 2 deletions backend/portal/helpers/password.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ def form_clean_password(self, password_field_name, strength: PasswordStrength):

def check_update_password(form, user, request, data):
changing_password = False
if data["password"] != "":
if data["new_password"] != "":
changing_password = True
user.set_password(data["password"])
user.set_password(data["new_password"])
user.save()
update_session_auth_hash(request, form.user)

Expand Down
2 changes: 2 additions & 0 deletions backend/portal/urls/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from .organisation import urlpatterns as organisation_urlpatterns
from .teacher.dashboard import urlpatterns as teach_dashboard_urlpatterns
from .registration import urlpatterns as registration_urlpatterns
from .student import urlpatterns as student_urlpatterns
from .teacher import urlpatterns as teacher_urlpatterns

urlpatterns = [
Expand All @@ -20,6 +21,7 @@
*home_urlpatterns,
*login_urlpatterns,
*registration_urlpatterns,
*student_urlpatterns,
*admin_urlpatterns,
*organisation_urlpatterns,
*teach_dashboard_urlpatterns,
Expand Down
2 changes: 2 additions & 0 deletions backend/portal/urls/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
teacher_password_reset,
password_reset_check_and_confirm,
delete_account,
verify_password,
)


Expand All @@ -29,4 +30,5 @@
delete_account,
name="delete_account",
),
path("verify-password/", verify_password, name="verify_password"),
]
Loading

0 comments on commit 2bf397a

Please sign in to comment.