Source code for app.factory

"""
Module containing creators for subsequent elements, such as assessments or submissions.
Adheres to 'factory' architectural pattern.

Factories to create new resources with lengthy methods.

@TeamAlpha 2018
CodeMarker
factory.py
"""

import json
import logging
import os

from django.conf import settings
from django.contrib.auth.models import Group, User
from django.core.exceptions import ObjectDoesNotExist
from django.core.files.storage import FileSystemStorage
from django.db import DataError
from django.http import (HttpResponseBadRequest, HttpResponseForbidden)
from django.utils.datastructures import MultiValueDictKeyError
from rest_framework.response import Response

from app.backup_service import create_backup
from app.models import Assessment, Course, InputGenerator, Resource, Submission

# Get an instance of a logger
logger = logging.getLogger(__name__)


[docs]def assessment_creator(self, serializer): """Method responsible for creating new assessments Raises: MultiValueDictKeyError -- Raised when not enough arguments have been passed Returns: HttpResponse -- Whether resource has been successfully created or not """ try: user = self.request.user name = self.request.POST.get("name", "") course_id = self.request.POST.get("course_id", "") description = self.request.POST.get("description", "") additional_help = self.request.POST.get("additional_help", "") deadline = self.request.POST.get('deadline', "") dynamic_input = self.request.POST["dynamicInput"] static_input = self.request.POST["staticInput"] languages = [x for x in json.loads( self.request.POST.get('languages', ""))] selected_language = self.request.POST.get('selected_language', "") num_of_static = int(self.request.POST.get('numOfStatic', "")) if static_input == "true": for i in range(int(num_of_static)): if 'inputFile'+str(i) not in self.request.FILES or 'outputFile'+str(i) not in self.request.FILES: raise MultiValueDictKeyError except MultiValueDictKeyError: return HttpResponseBadRequest("Looks like you have an empty field or an unknown file type.") except Exception as e: logger.error(e) return HttpResponseBadRequest("Unexpected error.") if name == "" or description == "": return HttpResponseBadRequest("Looks like you have an empty field.") if not user.is_staff: return HttpResponseForbidden("You are not allowed to create assessments") course = Course.objects.get(pk=course_id) assessment = Assessment( name=name, description=description, additional_help=additional_help, course=course, languages=languages, deadline=deadline, static_input=json.loads(static_input), dynamic_input=json.loads(dynamic_input), num_of_static=num_of_static) assessment.save() if static_input == "true": for i in range(int(num_of_static)): os.makedirs(os.path.join(settings.MEDIA_ROOT, str(assessment.id), 'static_inputs'), exist_ok=True) os.makedirs(os.path.join(settings.MEDIA_ROOT, str(assessment.id), 'expected_static_outputs'), exist_ok=True) fs = FileSystemStorage(location=os.path.join( settings.MEDIA_ROOT, str(assessment.id), 'static_inputs')) static_file = self.request.FILES['inputFile'+str(i)] fs.save(str(i), static_file) fs = FileSystemStorage(location=os.path.join( settings.MEDIA_ROOT, str(assessment.id), 'expected_static_outputs')) static_file = self.request.FILES['outputFile'+str(i)] fs.save(str(i), static_file) if dynamic_input == "true": resource_file = self.request.FILES['resource'] resource = Resource( filename=resource_file.name, status="start", assessment=assessment, language=selected_language) resource.save() os.makedirs(os.path.join(settings.MEDIA_ROOT, str(assessment.id), 'model_solutions', str(resource.id)), exist_ok=True) fs = FileSystemStorage(location=os.path.join( settings.MEDIA_ROOT, str(assessment.id), 'model_solutions', str(resource.id))) fs.save(resource_file.name, resource_file) input_generator_file = self.request.FILES['input_generator'] input_generator = InputGenerator( filename=input_generator_file.name, assessment=assessment, language=selected_language) input_generator.save() os.makedirs(os.path.join(settings.MEDIA_ROOT, str(assessment.id), 'input_generators', str(input_generator.id)), exist_ok=True) fs = FileSystemStorage(location=os.path.join( settings.MEDIA_ROOT, str(assessment.id), 'input_generators', str(input_generator.id))) fs.save(input_generator_file.name, input_generator_file) assessment.input_generator = input_generator assessment.resource = resource assessment.save() return Response(assessment.id, 201)
[docs]def course_creator(self, serializer): """ Dummy method for course creation, for future reference """ pass
[docs]def submission_creator(self, serializer): """Method for creating new submissions Arguments: serializer - passed from the views class, contains POST information Returns: HttpResponse - whether creation was successful or not """ try: assessment_id = self.request.POST['assessment_id'] language = self.request.POST['language'] submission_file = self.request.FILES['submission'] late = self.request.POST['late'] except MultiValueDictKeyError: return HttpResponseBadRequest("Looks like you have an empty upload field.") except DataError: return HttpResponseBadRequest("Looks like you have an empty dropdown field.") except Exception as e: print(e) logger.error(e) return HttpResponseBadRequest("Unexpected error.") if language == "undefined": return HttpResponseBadRequest("Looks like you have an empty language field.") assessment = Assessment.objects.get(id=assessment_id) course = Course.objects.get(id=assessment.course_id) if self.request.user not in course.students.all(): return HttpResponseForbidden("You cannot submit to a course you are not enrolled in.") submission = Submission( filename=submission_file.name, status="start", result="fail", info="Awaiting result...", marks=0, user_id=self.request.user.id, assessment_id=assessment_id, timeTaken=0, language=language, late=late) submission.save() os.makedirs(os.path.join(settings.MEDIA_ROOT, assessment_id, 'submissions', str(submission.id)), exist_ok=True) fs = FileSystemStorage(location=os.path.join( settings.MEDIA_ROOT, assessment_id, 'submissions', str(submission.id))) filename = fs.save(submission_file.name, submission_file) uploaded_file_url = fs.url(filename) return Response(submission.id, 201)
[docs]def import_users(request): """Given an uploaded CSV file containing list of users, import them into DB Arguments: request - request containing the uploaded CSV file Returns: request -- Whether the import was successful or not """ # Create backup of the current userbase before import create_backup() # Parse uploaded CSV file csvfile = request.FILES.get("csv").read().decode("utf-8") logger.error(csvfile) # Iterate the CSV file for entries for row in csvfile.splitlines(): userdata = row.split(',') # Delete duplicated users first try: user = User.objects.get(username=userdata[2]) user.delete() except ObjectDoesNotExist: logger.debug("Requested user doesn't exist. Ignoring") # Create and save new user based on the provided information in CSV user = User.objects.create_user(first_name=userdata[0], last_name=userdata[1], username=userdata[2], email=userdata[3], password=userdata[4]) user.save() # Add student to the group group = Group.objects.get(name='student') group.user_set.add(user) group.save() courses = userdata[5].split() # Add student to all courses specified in the CSV file try: for course in courses: course_model = Course.objects.get(name=course) course_model.students.add(user) course_model.save() except ObjectDoesNotExist: # Something went wrong, report the issue and halt the import return Response("One of the courses doesn't exist!", 400) # All was fine, return OK return Response(status=200)