""" This file allows us to write our own custom views for our HTML templates""" import csv import re from io import StringIO from django.shortcuts import render, redirect from django.forms import modelform_factory from django.contrib.auth.decorators import login_required from django.contrib import messages from django.db import IntegrityError from django.views.generic import DetailView from django.utils.decorators import method_decorator from .models import Semester_Class, Student from .models import Grouped_Student, Assignment from .utils.gatherInfo import gatherStudents from .utils.run import input_interface from .utils import constants from .forms import UploadCSVForm, CreateGroupForm from .forms import CustomUserCreationForm from .forms import AssignmentForm, StudentForm, GroupForm # pylint: disable=too-many-ancestors @method_decorator(login_required, name="dispatch") class AssignmentView(DetailView): """ This view allows there to be a unique page for each assignment it passes information to the template to customize each assignment page""" model = Assignment def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) instance = self.object group_list = list( # pylint: disable=no-member Grouped_Student.objects.filter(assignment_id=instance).order_by( "group_name" ) ) groupNames = [] for g in group_list: if g.group_name not in groupNames: groupNames.append(g.group_name) context["groups"] = group_list context["groupNames"] = groupNames return context # Collects information from the form and passes it to upload_csv.html def upload_csv(request): """ POST request for handling CSV upload and grouping students """ if request.method == "POST": form = UploadCSVForm(request.POST, request.FILES) if form.is_valid(): responses = parse_uploaded_csv(request.FILES["student_data"]) if request.FILES.get("student_preferences"): preferences = parse_uploaded_csv( request.FILES["student_preferences"], as_dict=True ) else: preferences = None numgrp = form.cleaned_data["numgrp"] preferences_weight = form.cleaned_data["preferences_weight"] preferences_weight_match = form.cleaned_data["preferences_weight_match"] groups = input_interface( responses, method=constants.ALGORITHM_GRAPH, num_group=numgrp, preferences=preferences, preferences_weight=preferences_weight, preferences_weight_match=preferences_weight_match, ) return render( request, "gatorgrouper/viewing-groups-csv.html", {"groups": groups} ) else: form = UploadCSVForm() return render(request, "gatorgrouper/upload_csv.html", {"form": form}) def parse_uploaded_csv(csvfile, as_dict=False): """ Transform uploded CSV data into list of student responses: [["student name", True, False, ...], ...] With the as_dict parameter set to True, transforms the CSV data into a dictionary of sets: {"student name": {True, False, ...}, ...} """ f = StringIO(csvfile.read().decode("utf-8")) csvdata = list(csv.reader(f, delimiter=",")) # transform into desired output responses = list() responses_dict = {} for record in csvdata: if as_dict: # Create key in responses dictionary responses_dict[record[0]] = set() temp = list() temp.append(record[0].replace('"', "")) for value in record[1:]: if value.lower() == "true": temp.append(True) elif value.lower() == "false": temp.append(False) # pylint: disable=bad-continuation elif re.match(r"[+-]?([0-9]*[.])?[0-9]+", value): # Match a float with regex temp.append(float(value)) else: # Keep the value as a string if no other type matches temp.append(value) if as_dict: # Assign the value to the responses set for this row responses_dict[record[0]].add(value) responses.append(temp) return responses if not as_dict else responses_dict def register(request): """ This view loads the register page and handles the form """ if request.method == "POST": form = CustomUserCreationForm(request.POST) if form.is_valid(): form.save() first_name = form.cleaned_data.get("first_name") last_name = form.cleaned_data.get("last_name") # message = "Account created for " + first_name + " " + last_name # messages.success(request, message=message) messages.success(request, f"Account created for {first_name} {last_name}") return redirect("login") else: form = CustomUserCreationForm() return render( request, "gatorgrouper/register.html", {"title": "Register", "form": form} ) # Collects information regarding the Professor, classes, assignment_list, and Students # and passes it to profile.html @login_required def profile(request): """ Loads the user in correspondence through the professor and Returns a list of the classes, assignments, and students related to it """ current_professor = request.user # pylint: disable=no-member classes = list(Semester_Class.objects.filter(professor_id=current_professor)) # pylint: disable=no-member assignment_list = list(Assignment.objects.all()) # pylint: disable=no-member students = list(Student.objects.all()) return render( request, "gatorgrouper/profile.html", { "title": "Profile", "all_classes": classes, "all_assignments": assignment_list, "all_students": students, }, ) def handle_uploaded_file(csvfile): """ Transform uploded CSV data into list of student responses: [["student name", True, False, ...]] """ # get rid of decode because it's already default in python3 # f = StringIO(csvfile.read().decode("utf-8")) f = StringIO(csvfile.read()) csvdata = list(csv.reader(f, delimiter=",")) # transform into desired output responses = list() for record in csvdata: temp = list() temp.append(record[0].replace('"', "")) for value in record[1:]: if value.lower() == "true": temp.append(True) elif value.lower() == "false": temp.append(False) responses.append(temp) return responses # Returns the user to the home page def home(request): """ Homepage view """ return render(request, "gatorgrouper/home.html", {"title": "Home"}) # return HttpResponse # Function to view the list of classes provided by the Professor and passed to # classes.html @login_required def create_classes(request): """ Create classes view """ ClassFormSet = modelform_factory( Semester_Class, fields=("semester", "department", "class_number", "class_section"), ) if request.method == "POST": formset = ClassFormSet(request.POST) if formset.is_valid(): stock = formset.save(commit=False) stock.professor_id = request.user stock.save() messages.success(request, f"Class Added") return redirect("profile") else: formset = ClassFormSet() return render( request, "gatorgrouper/classes.html", {"title": "Create Classes", "formset": formset}, ) # Allows the user to view the list of assignments @login_required def assignments(request): """ Create assignments view """ if request.method == "POST": formset = AssignmentForm(request.user, request.POST) if formset.is_valid(): stock = formset.save(commit=False) stock.professor_id = request.user stock.save() messages.success(request, f"Assignment Successfully Created") return redirect("profile") else: formset = AssignmentForm(request.user) return render( request, "gatorgrouper/assignments.html", {"title": "Create Assignments", "formset": formset}, ) # Using the survey to get the grouping preference for the students @login_required def survey(request): """ Student's grouping preference? """ return render(request, "gatorgrouper/survey.html", {"title": "Survey"}) # Function allows displaying current students and add students based on the request @login_required def add_students(request): """ Used to display current students in roster and provides option to add students """ if request.method == "POST": formset = StudentForm(request.user, request.POST) if formset.is_valid(): formset.save() messages.success(request, f"Student Successfully Created") return redirect("add-students") else: formset = StudentForm(request.user) return render( request, "gatorgrouper/add-students.html", {"title": "Add a Student", "formset": formset}, ) # Allows to create a group using the rrobin method # pylint: disable=too-many-locals @login_required def create_groups(request): """ Finds all the required information, call GatorGrouper with the provided information and displays it to the user and saves it if clicked 'save' """ groups = [] if not request.user.is_authenticated: return redirect("upload-csv") # pylint: disable=too-many-nested-blocks # conditional logic for a 'POST' request if request.method == "POST": formset = GroupForm(request.user, request.POST) groupNum = CreateGroupForm(request.POST) # Conditional logic to see if the submitted forms are valid if groupNum.is_valid() and formset.is_valid(): assignment_obj = formset.cleaned_data.get("assignment_id") class_id_num = assignment_obj.class_id.class_id num_of_groups = groupNum.cleaned_data.get("numgrp") student_list_dict = gatherStudents(class_id_num) # pylint: disable=unused-variable # Dictionary to hold student object and student names and returns # the dictionary student_list = [] # Finds the student object based on the student name and finds the assignment id for name, obj in student_list_dict.items(): student_list.append(name) groups = input_interface( student_list, constants.ALGORITHM_ROUND_ROBIN, num_of_groups ) if request.POST["button"] == "save": counter = 1 for group in groups: group_name = "Group " + str(counter) # For loop to go through each student in the group and saves # each student with the required information for student in group: try: s = Grouped_Student( assignment_id=assignment_obj, student_id=student_list_dict[student], group_name=group_name, ) # Saving all the above information s.save() # To check if the assignment_id and student_id hold unique # values. If not, send error message except IntegrityError: messages.error( request, f"This assignment already has a group associated " + f"with it.\nPlease Try again", ) return redirect("create-groups") # Successful message sent once it puts every student and every # group inside the database counter += 1 messages.success( request, f"The groups for this assignment have been saved. To see them," + f"visit the view groups page", ) # condition to pass the empty form at the beginning else: formset = GroupForm(request.user) groupNum = CreateGroupForm() return render( request, "gatorgrouper/create-groups.html", { "title": "Create Groups", "formset": formset, "group_list": groups, "groupNum": groupNum, }, )