diff --git a/gatorgrouper/forms.py b/gatorgrouper/forms.py index ee3672a5..f9d892d9 100644 --- a/gatorgrouper/forms.py +++ b/gatorgrouper/forms.py @@ -31,9 +31,22 @@ class Meta: class UploadCSVForm(forms.Form): """ Form enabling the uploading of a CSV file to be used for grouping """ - file = forms.FileField(label="Student data CSV file") - numgrp = forms.IntegerField( - min_value=2, max_value=25, label="Number of groups to create" + student_data = forms.FileField(label="Student data CSV file") + student_preferences = forms.FileField( + label="Student preferences CSV file", required=False + ) + numgrp = forms.TypedChoiceField( + choices=[ + (str(2 ** i), str(2 ** i)) for i in range(1, 5) + ], # Restrict choices to powers of 2 + coerce=int, + label="Number of groups to create", + ) + preferences_weight = forms.FloatField( + label="Importance of an unmatched preference", initial=1.1, min_value=1.0 + ) + preferences_weight_match = forms.FloatField( + label="Importance of a matched preference", initial=1.25, min_value=1.0 ) diff --git a/gatorgrouper/templates/gatorgrouper/upload_csv.html b/gatorgrouper/templates/gatorgrouper/upload_csv.html index f0ae8252..4cdcd952 100644 --- a/gatorgrouper/templates/gatorgrouper/upload_csv.html +++ b/gatorgrouper/templates/gatorgrouper/upload_csv.html @@ -4,17 +4,32 @@
- Create a New Assignment -
+ Create Groups +

+ GatorGrouper supports creating groups via an uploaded CSV file. To save your data and groups across multiple sessions, create an account today! +

+ {% csrf_token %}
- {{ form.file.label_tag }} - + {{ form.student_data.label_tag }} + +
+
+ {{ form.student_preferences.label_tag }} +
{{ form.numgrp.label_tag }} {{ form.numgrp }}
+
+ {{ form.preferences_weight.label_tag }} + {{ form.preferences_weight }} +
+
+ {{ form.preferences_weight_match.label_tag }} + {{ form.preferences_weight_match }} +
Back to Dashboard diff --git a/gatorgrouper/templates/gatorgrouper/viewing-groups-csv.html b/gatorgrouper/templates/gatorgrouper/viewing-groups-csv.html new file mode 100644 index 00000000..74c01fbd --- /dev/null +++ b/gatorgrouper/templates/gatorgrouper/viewing-groups-csv.html @@ -0,0 +1,17 @@ +{% extends "gatorgrouper/base.html" %} +{% block content %} + +
+

Team Groupings

+
+ {% for group in groups %} +

Team #{{ forloop.counter }}

+
    + {% for student in group %} +
  • {{ student | first }}
  • + {% endfor %} +
+ {% endfor %} +
+
+{% endblock %} diff --git a/gatorgrouper/templates/upload.html b/gatorgrouper/templates/upload.html index b5830e5c..4704dffb 100644 --- a/gatorgrouper/templates/upload.html +++ b/gatorgrouper/templates/upload.html @@ -1,6 +1,6 @@ - + {% csrf_token %} {{ form }} diff --git a/gatorgrouper/urls.py b/gatorgrouper/urls.py index 74cea764..5a65209d 100644 --- a/gatorgrouper/urls.py +++ b/gatorgrouper/urls.py @@ -16,4 +16,5 @@ path("group-result/", views.groupResult, name="Gatorgrouper-groups"), path("add-students/", views.add_students, name="add-students"), path("create-groups/", views.create_groups, name="create-groups"), + path("upload-csv/", views.upload_csv, name="upload-csv"), ] diff --git a/gatorgrouper/views.py b/gatorgrouper/views.py index 8dd30e2f..7992eb6d 100644 --- a/gatorgrouper/views.py +++ b/gatorgrouper/views.py @@ -1,5 +1,6 @@ """ This is undocumented """ import csv +import re from io import StringIO from django.shortcuts import render, redirect from django.forms import modelform_factory @@ -11,6 +12,7 @@ from .models import Semester_Class, Student from .models import Grouped_Student, Assignment from .utils.gatherInfo import gatherStudents +from .utils.group_graph import group_graph_partition from .utils.group_rrobin import group_rrobin_num_group from .forms import UploadCSVForm, CreateGroupForm from .forms import CustomUserCreationForm @@ -21,18 +23,67 @@ def upload_csv(request): if request.method == "POST": form = UploadCSVForm(request.POST, request.FILES) if form.is_valid(): - responses = handle_uploaded_file(request.FILES["file"]) + 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"] - groups = group_rrobin_num_group(responses, numgrp) + preferences_weight = form.cleaned_data["preferences_weight"] + preferences_weight_match = form.cleaned_data["preferences_weight_match"] + groups = group_graph_partition( + responses, + numgrp, + preferences=preferences, + preferences_weight=preferences_weight, + preferences_weight_match=preferences_weight_match, + ) return render( - request, "gatorgrouper/viewing-groups.html", {"groups": groups} + request, "gatorgrouper/viewing-groups-csv.html", {"groups": groups} ) else: form = UploadCSVForm() return render(request, "gatorgrouper/upload_csv.html", {"form": form}) -# Create your views here. +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: + responses_dict[record[0]] = set() # Create key in responses dictionary + 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) + elif re.match( + r"[+-]?([0-9]*[.])?[0-9]+", value # pylint: disable=bad-continuation + ): # 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": @@ -72,28 +123,6 @@ def profile(request): ) -def handle_uploaded_file(csvfile): - """ - Transform uploded CSV data into list of student responses: - [["student name", True, False, ...]] - """ - f = StringIO(csvfile.read().decode("utf-8")) - 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 - - def home(request): """ Homepage view """ @@ -214,13 +243,14 @@ def add_students(request): ) -@login_required def create_groups(request): # pylint: disable=too-many-locals """ Created groups using gatorgrouper functions """ GroupedStudentFormSet = modelform_factory( Grouped_Student, fields=("assignment_id",) ) groups = [] + if not request.user.is_authenticated: + return redirect("upload-csv") # pylint: disable=too-many-nested-blocks if request.method == "POST": formset = GroupedStudentFormSet(request.POST)