Repository: ocadotechnology/codeforlife-portal

Issue:
Class page - delete students



Discussion:

Resolving patch:
diff --git a/portal/urls.py b/portal/urls.py
--- a/portal/urls.py
+++ b/portal/urls.py
@@ -71,7 +71,7 @@
 from portal.views.organisation_new import organisation_fuzzy_lookup_new, organisation_manage_new
 from portal.views.teacher.teach_new import teacher_classes_new, teacher_class_new, teacher_view_class, \
     teacher_delete_class_new, teacher_edit_class_new, teacher_move_class_new, teacher_edit_student_new, \
-    teacher_student_reset_new, materials_viewer_new, teacher_print_reminder_cards_new
+    teacher_student_reset_new, materials_viewer_new, teacher_print_reminder_cards_new, teacher_delete_students_new
 from portal.views.teacher.dashboard import dashboard_manage, organisation_allow_join_new, organisation_deny_join_new, \
     organisation_kick_new, organisation_toggle_admin_new, teacher_disable_2FA_new
 
@@ -231,6 +231,7 @@
     url(r'^redesign/teach/dashboard/deny_join/(?P<pk>[0-9]+)/$', organisation_deny_join_new, name='organisation_deny_join_new'),
     url(r'^redesign/teach/class/(?P<access_code>[A-Z0-9]+)/$', teacher_view_class, name='view_class'),
     url(r'^redesign/teach/class/delete/(?P<access_code>[A-Z0-9]+)/$', teacher_delete_class_new, name='teacher_delete_class_new'),
+    url(r'^redesign/teach/class/(?P<access_code>[A-Z0-9]+)/students/delete/$', teacher_delete_students_new, name='teacher_delete_students_new'),
     url(r'^redesign/teach/class/edit/(?P<access_code>[A-Z0-9]+)/$', teacher_edit_class_new, name='teacher_edit_class_new'),
     url(r'^redesign/teach/class/student/edit/(?P<pk>[0-9]+)/$', teacher_edit_student_new, name='teacher_edit_student_new'),
     url(r'^redesign/teach/class/student/reset/(?P<pk>[0-9]+)/$', teacher_student_reset_new, name='teacher_student_reset_new'),
diff --git a/portal/views/teacher/teach_new.py b/portal/views/teacher/teach_new.py
--- a/portal/views/teacher/teach_new.py
+++ b/portal/views/teacher/teach_new.py
@@ -35,7 +35,7 @@
 # program; modified versions of the program must be marked as such and not
 # identified as the original program.
 import json
-
+from functools import partial, wraps
 from datetime import timedelta
 
 from django.conf import settings
@@ -45,6 +45,7 @@
 from django.contrib import messages as messages
 from django.contrib.auth.decorators import login_required, user_passes_test
 from django.contrib.staticfiles.storage import staticfiles_storage
+from django.forms.formsets import formset_factory
 from django.utils import timezone
 
 from reportlab.pdfgen import canvas
@@ -54,9 +55,9 @@
 from reportlab.platypus import Paragraph
 from reportlab.lib.styles import ParagraphStyle
 
-from portal.models import Class, Student, Teacher
-from portal.forms.teach_new import ClassCreationForm, StudentCreationForm, ClassMoveForm, ClassEditForm, \
-    TeacherEditStudentForm, TeacherSetStudentPass
+from portal.models import Teacher, Class, Student
+from portal.forms.teach_new import TeacherEditAccountForm, ClassCreationForm, ClassEditForm, ClassMoveForm, \
+    TeacherEditStudentForm, TeacherSetStudentPass, StudentCreationForm
 from portal.permissions import logged_in_as_teacher
 from portal.helpers.generators import generate_access_code, generate_password
 from portal.views.teacher.pdfs import PDF_DATA
@@ -267,6 +268,26 @@ def teacher_delete_class_new(request, access_code):
     return HttpResponseRedirect(reverse_lazy('dashboard'))
 
 
+@login_required(login_url=reverse_lazy('login_new'))
+@user_passes_test(logged_in_as_teacher, login_url=reverse_lazy('login_new'))
+def teacher_delete_students_new(request, access_code):
+    klass = get_object_or_404(Class, access_code=access_code)
+
+    # check user is authorised to deal with class
+    if request.user.new_teacher != klass.teacher:
+        raise Http404
+
+    # get student objects for students to be deleted, confirming they are in the class
+    student_ids = json.loads(request.POST.get('transfer_students', '[]'))
+    students = [get_object_or_404(Student, id=i, class_field=klass) for i in student_ids]
+
+    # Delete all of the students
+    for student in students:
+        student.new_user.delete()
+
+    return HttpResponseRedirect(reverse_lazy('view_class', kwargs={'access_code': access_code}))
+
+
 @login_required(login_url=reverse_lazy('login_new'))
 @user_passes_test(logged_in_as_teacher, login_url=reverse_lazy('login_new'))
 def teacher_edit_class_new(request, access_code):


Test patch:
diff --git a/portal/tests/pageObjects/portal/teach/class_page_new.py b/portal/tests/pageObjects/portal/teach/class_page_new.py
--- a/portal/tests/pageObjects/portal/teach/class_page_new.py
+++ b/portal/tests/pageObjects/portal/teach/class_page_new.py
@@ -66,28 +66,41 @@ def delete_class(self):
         self.browser.find_element_by_id('deleteClass').click()
         return self
 
+    def delete_students(self):
+        self.browser.find_element_by_id('deleteSelectedStudents').click()
+        return self
+
     def cancel_dialog(self):
         self.browser.find_element_by_xpath(
             "//div[contains(@class,'ui-dialog')]//span[contains(text(),'Cancel')]").click()
         return self
 
-    def confirm_dialog(self):
+    def confirm_delete_class_dialog(self):
         self._click_confirm()
 
         return dashboard_page_new.TeachDashboardPage(self.browser)
 
-    def is_dialog_showing(self):
-        return self.browser.find_element_by_xpath("//div[contains(@class,'ui-dialog')]").is_displayed()
+    def confirm_delete_student_dialog(self):
+        self._click_confirm()
+
+        return self
 
     def confirm_dialog_expect_error(self):
         self._click_confirm()
 
         return self
 
+    def is_dialog_showing(self):
+        return self.browser.find_element_by_xpath("//div[contains(@class,'ui-dialog')]").is_displayed()
+
     def _click_confirm(self):
         self.browser.find_element_by_xpath(
             "//div[contains(@class,'ui-dialog')]//span[contains(text(),'Confirm')]").click()
 
+    def toggle_select_student(self):
+        self.browser.find_element_by_id("student_checkbox").click()
+        return self
+
     def wait_for_messages(self):
         self.wait_for_element_by_id('messages')
 
diff --git a/portal/tests/test_class_new.py b/portal/tests/test_class_new.py
--- a/portal/tests/test_class_new.py
+++ b/portal/tests/test_class_new.py
@@ -84,6 +84,23 @@ def test_create_dashboard(self):
 
         assert is_class_created_message_showing(selenium, class_name)
 
+    def test_delete_empty(self):
+        email, password = signup_teacher_directly()
+        create_organisation_directly(email)
+        _, class_name, access_code = create_class_directly(email)
+        create_school_student_directly(access_code)
+
+        page = self.go_to_homepage().go_to_login_page().login(email, password)
+        page = page.go_to_class_page()
+
+        page = page.toggle_select_student().delete_students()
+        page = page.confirm_delete_student_dialog()
+        page = page.delete_class()
+        assert page.is_dialog_showing()
+        page = page.confirm_delete_class_dialog()
+        assert page.__class__.__name__ == 'TeachDashboardPage'
+        assert page.does_not_have_classes()
+
     def test_delete_nonempty(self):
         email, password = signup_teacher_directly()
         create_organisation_directly(email)
diff --git a/portal/tests/test_teacher_student_new.py b/portal/tests/test_teacher_student_new.py
--- a/portal/tests/test_teacher_student_new.py
+++ b/portal/tests/test_teacher_student_new.py
@@ -162,3 +162,20 @@ def test_generate_random_student_password(self):
         page = page.click_generate_password_button()
 
         assert page.__class__.__name__ == 'EditStudentPasswordPage'
+
+    def test_delete(self):
+        email, password = signup_teacher_directly()
+        create_organisation_directly(email)
+        _, class_name, access_code = create_class_directly(email)
+        student_name, student_password, _ = create_school_student_directly(access_code)
+
+        selenium.get(self.live_server_url + "/portal/redesign/home")
+        page = HomePage(selenium).go_to_login_page().login(email, password)
+        page = page.go_to_class_page()
+        assert page.student_exists(student_name)
+
+        page = page.toggle_select_student().delete_students()
+        assert page.is_dialog_showing()
+        page = page.confirm_delete_student_dialog()
+
+        assert not page.student_exists(student_name)