Repository: ocadotechnology/codeforlife-portal

Issue:
Registration acceptation link for indep. students should go to new page
Right now when an independent student registers an account, they are redirected to the old message in the old home page


Discussion:

Resolving patch:
diff --git a/portal/forms/play_new.py b/portal/forms/play_new.py
--- a/portal/forms/play_new.py
+++ b/portal/forms/play_new.py
@@ -135,3 +135,38 @@ def clean(self):
             raise forms.ValidationError("Your passwords do not match")
 
         return self.cleaned_data
+
+
+class IndependentStudentLoginForm(forms.Form):
+    username = forms.CharField(
+        label='Username',
+        widget=forms.TextInput(attrs={'placeholder': "rosie_f"}))
+    password = forms.CharField(
+        label='Password',
+        widget=forms.PasswordInput())
+
+    def clean(self):
+        if self.has_error('recaptcha'):
+            raise forms.ValidationError("Incorrect username, password or captcha")
+
+        username = self.cleaned_data.get('username', None)
+        password = self.cleaned_data.get('password', None)
+
+        if username and password:
+            students = Student.objects.filter(class_field=None, new_user__username=username)
+            if not students.exists():
+                raise forms.ValidationError("Incorrect username or password")
+
+            user = authenticate(username=username, password=password)
+
+            self.check_for_errors(user)
+
+            self.user = user
+
+        return self.cleaned_data
+
+    def check_for_errors(self, user):
+        if user is None:
+            raise forms.ValidationError("Incorrect username or password")
+        if not user.is_active:
+            raise forms.ValidationError("This user account has been deactivated")
diff --git a/portal/views/email_new.py b/portal/views/email_new.py
--- a/portal/views/email_new.py
+++ b/portal/views/email_new.py
@@ -67,7 +67,7 @@ def verify_email_new(request, token):
     messages.success(request, 'Your email address was successfully verified, please log in.')
 
     if hasattr(user.userprofile, 'student'):
-        return HttpResponseRedirect(reverse_lazy('play'))
+        return HttpResponseRedirect(reverse_lazy('login_new'))
     if hasattr(user.userprofile, 'teacher'):
         return HttpResponseRedirect(reverse_lazy('onboarding-organisation'))
 
diff --git a/portal/views/home_new.py b/portal/views/home_new.py
--- a/portal/views/home_new.py
+++ b/portal/views/home_new.py
@@ -47,7 +47,7 @@
 
 from portal.models import Teacher, Class, Student
 from portal.forms.teach_new import TeacherSignupForm, TeacherLoginForm
-from portal.forms.play_new import StudentLoginForm, StudentSignupForm
+from portal.forms.play_new import StudentLoginForm, IndependentStudentLoginForm, StudentSignupForm
 from portal.helpers.emails_new import send_verification_email, is_verified
 from portal.utils import using_two_factor
 from portal import app_settings
@@ -112,31 +112,44 @@ def render_login_form(request):
 
     school_login_form = OutputStudentLoginForm(prefix='login')
 
+    IndependentStudentLoginFormWithCaptcha = partial(create_form_subclass_with_recaptcha(IndependentStudentLoginForm, recaptcha_client), request)
+    InputIndependentStudentLoginForm = compute_indep_student_input_login_form(IndependentStudentLoginFormWithCaptcha, student_limits, student_captcha_limit, student_name_captcha_limit)
+    OutputIndependentStudentLoginForm = compute_indep_student_output_login_form(IndependentStudentLoginFormWithCaptcha, student_limits, student_captcha_limit, student_name_captcha_limit)
+
+    independent_student_login_form = IndependentStudentLoginForm(prefix='independent_student')
+    independent_student_view = False
+
+    render_dict = {
+        'login_form': login_form,
+        'school_login_form': school_login_form,
+        'independent_student_login_form': independent_student_login_form,
+        'independent_student_view': independent_student_view,
+        'logged_in_as_teacher': is_logged_in_as_teacher(request),
+    }
+
     if request.method == 'POST':
         if 'school_login' in request.POST:
-            school_login_form = InputStudentLoginForm(request.POST, prefix='login')
+            form = InputStudentLoginForm(request.POST, prefix="login")
+            process_form = process_student_login_form
+            render_dict['school_login_form'] = OutputStudentLoginForm(request.POST, prefix='login')
 
-            if school_login_form.is_valid():
-                return process_student_login_form(request, school_login_form)
-
-            else:
-                school_login_form = OutputStudentLoginForm(request.POST, prefix='login')
-                invalid_form = True
+        elif 'independent_student_login' in request.POST:
+            form = InputIndependentStudentLoginForm(request.POST, prefix='independent_student')
+            process_form = process_indep_student_login_form
+            render_dict['independent_student_login_form'] = OutputIndependentStudentLoginForm(request.POST, prefix='independent_student')
+            render_dict['independent_student_view'] = True
 
         else:
-            login_form = InputLoginForm(request.POST, prefix='login')
-            if login_form.is_valid():
-                return process_login_form(request, login_form)
+            form = InputLoginForm(request.POST, prefix='login')
+            process_form = process_login_form
+            render_dict['login_form'] = OutputLoginForm(request.POST, prefix='login')
 
-            else:
-                login_form = OutputLoginForm(request.POST, prefix='login')
-                invalid_form = True
+        if form.is_valid():
+            return process_form(request, form)
+        else:
+            invalid_form = True
 
-    res = render(request, 'redesign/login.html', {
-        'login_form': login_form,
-        'school_login_form': school_login_form,
-        'logged_in_as_teacher': is_logged_in_as_teacher(request),
-    })
+    res = render(request, 'redesign/login.html', render_dict)
 
     res.count = invalid_form
     return res
@@ -213,6 +226,16 @@ def compute_student_output_login_form(StudentLoginFormWithCaptcha, limits, ip_ca
     return OutputStudentLoginForm
 
 
+def compute_indep_student_input_login_form(IndependentStudentLoginFormWithCaptcha, limits, ip_captcha_limit, name_captcha_limit):
+    InputIndependentStudentLoginForm = IndependentStudentLoginFormWithCaptcha if compute_student_use_captcha(limits, ip_captcha_limit, name_captcha_limit) else IndependentStudentLoginForm
+    return InputIndependentStudentLoginForm
+
+
+def compute_indep_student_output_login_form(IndependentStudentLoginFormWithCaptcha, limits, ip_captcha_limit, name_captcha_limit):
+    OutputIndependentStudentLoginForm = IndependentStudentLoginFormWithCaptcha if compute_student_should_use_captcha(limits, ip_captcha_limit, name_captcha_limit) else IndependentStudentLoginForm
+    return OutputIndependentStudentLoginForm
+
+
 def process_login_form(request, login_form):
     user = login_form.user
     if not is_verified(user):
@@ -255,6 +278,22 @@ def process_student_login_form(request, school_login_form):
     return HttpResponseRedirect(reverse_lazy('student_details'))
 
 
+def process_indep_student_login_form(request, independent_student_login_form):
+    user = independent_student_login_form.user
+    if not is_verified(user):
+        send_verification_email(request, user)
+        return render(request, 'redesign/email_verification_needed.html',
+                      {'user': user})
+
+    login(request, independent_student_login_form.user)
+
+    next_url = request.GET.get('next', None)
+    if next_url:
+        return HttpResponseRedirect(next_url)
+
+    return HttpResponseRedirect(reverse_lazy('student_details'))
+
+
 def process_signup_form(request, data):
     teacher = Teacher.objects.factory(
         title=data['teacher_title'],


Test patch:
diff --git a/portal/tests/pageObjects/portal/login_page.py b/portal/tests/pageObjects/portal/login_page.py
--- a/portal/tests/pageObjects/portal/login_page.py
+++ b/portal/tests/pageObjects/portal/login_page.py
@@ -77,6 +77,11 @@ def student_login(self, name, access_code, password):
 
         return play_page.PlayDashboardPage(self.browser)
 
+    def independent_student_login(self, username, password):
+        self._independent_student_login(username, password)
+
+        return play_page.PlayDashboardPage(self.browser)
+
     def login_failure(self, email, password):
         self._login(email, password)
         return self
@@ -85,6 +90,10 @@ def student_login_failure(self, name, access_code, password):
         self._student_login(name, access_code, password)
         return self
 
+    def independent_student_login_failure(self, username, password):
+        self._independent_student_login(username, password)
+        return self
+
     def _login(self, email, password):
         self.browser.find_element_by_id('id_login-teacher_email').send_keys(email)
         self.browser.find_element_by_id('id_login-teacher_password').send_keys(password)
@@ -96,6 +105,13 @@ def _student_login(self, name, access_code, password):
         self.browser.find_element_by_id('id_login-password').send_keys(password)
         self.browser.find_element_by_name('school_login').click()
 
+    def _independent_student_login(self, username, password):
+        self.browser.find_element_by_id('switchToIndependentStudent').click()
+
+        self.browser.find_element_by_id('id_independent_student-username').send_keys(username)
+        self.browser.find_element_by_id('id_independent_student-password').send_keys(password)
+        self.browser.find_element_by_name('independent_student_login').click()
+
     def has_login_failed(self):
         if not self.element_exists_by_css('.errorlist'):
             return False
@@ -111,3 +127,11 @@ def has_student_login_failed(self):
         errors = self.browser.find_element_by_id('form-login-school').find_element_by_class_name('errorlist').text
         error = 'Invalid name, class access code or password'
         return error in errors
+
+    def has_independent_student_login_failed(self):
+        if not self.element_exists_by_css('.errorlist'):
+            return False
+
+        errors = self.browser.find_element_by_id('independent_student_login_form').find_element_by_class_name('errorlist').text
+        error = 'Incorrect username or password'
+        return error in errors
diff --git a/portal/tests/test_independent_student_new.py b/portal/tests/test_independent_student_new.py
--- a/portal/tests/test_independent_student_new.py
+++ b/portal/tests/test_independent_student_new.py
@@ -55,3 +55,21 @@ def test_failed_signup(self):
         page = self.go_to_homepage()
         page = submit_independent_student_signup_form(page, password='test')
         assert page.has_independent_student_signup_failed()
+
+    def test_login_failure(self):
+        page = self.go_to_homepage()
+        page = page.go_to_login_page()
+        page = page.independent_student_login_failure('Non existent username', 'Incorrect password')
+
+        assert page.has_independent_student_login_failed()
+
+    def test_login_success(self):
+        page = self.go_to_homepage()
+        page, name, username, email, password = create_independent_student(page)
+        page = page.independent_student_login(username, password)
+        assert page.__class__.__name__ == 'PlayDashboardPage'
+
+        page = page.go_to_account_page()
+        assert page.check_account_details({
+            'name': name
+        })
diff --git a/portal/tests/utils/email.py b/portal/tests/utils/email.py
--- a/portal/tests/utils/email.py
+++ b/portal/tests/utils/email.py
@@ -56,6 +56,12 @@ def follow_verify_email_link_to_play(page, email):
     return go_to_play_page(page.browser)
 
 
+def follow_verify_email_link_to_login(page, email):
+    _follow_verify_email_link(page, email)
+
+    return go_to_login_page(page.browser)
+
+
 def _follow_verify_email_link(page, email):
     message = str(email.message())
     prefix = '<p>Please go to <a href="'
diff --git a/portal/tests/utils/student_new.py b/portal/tests/utils/student_new.py
--- a/portal/tests/utils/student_new.py
+++ b/portal/tests/utils/student_new.py
@@ -102,7 +102,7 @@ def create_independent_student(page):
 
     page = page.return_to_home_page()
 
-    page = email.follow_verify_email_link_to_play(page, mail.outbox[0])
+    page = email.follow_verify_email_link_to_login(page, mail.outbox[0])
     mail.outbox = []
 
     return page, name, username, email_address, password