Esempio n. 1
0
def code(request):
    #TODO: Can this be moved to a decorator?
    if not 'outcome_url' in request.session or not 'course_name' in request.session \
     or not 'assignment_name' in request.session:
        #This error message should be as it is, as the site can't load properly without these parameters
        return HttpResponse("Missing parameters")

    message = ""
    error_message = ""
    template = "grade/editor.html"

    # Fetch the object matching the assignment name given by the LTI
    assignment_name = request.session['assignment_name']
    course_name = request.session['course_name']

    try:
        assignment = Assignment.objects.get(
            name=assignment_name, course__name=request.session['course_name'])
    except Assignment.DoesNotExist:
        logging.warning('Assignment not found: ' + assignment_name)
        #This error message should be as it is, as the site can't load properly without this parameter
        return HttpResponse("No assignment found!")

    attempts_left = None
    #If assignment.attempts > 0, then the number of attempts is limited
    if assignment.attempts > 0:
        try:
            user_attempts = UserAttempts.objects.get(
                user__username=request.user.username,
                assignment__id=assignment.id)
            attempts_left = user_attempts.attempts
        except UserAttempts.DoesNotExist:
            #User is trying this for the first time, create new db object that connects the user object to amount of
            #attempts the user has left to try this assignment
            to_add = UserAttempts()
            to_add.user = request.user
            to_add.assignment = assignment
            to_add.attempts = assignment.attempts
            attempts_left = assignment.attempts
            to_add.save()

    if request.method == 'POST':
        if assignment.variable_injection == False:
            form = EditorForm(request.POST)
        else:
            form = DoubleEditorForm(request.POST)
            template = "grade/editor_parameter_inject.html"

        if form.is_valid():
            if not assignment.variable_injection:
                save_code(course_name, assignment_name, request.user.username,
                          form.cleaned_data['text'])
            else:
                save_code(
                    course_name, assignment_name, request.user.username,
                    form.cleaned_data['parameters'] + '\n' +
                    form.cleaned_data['text'])
            #docker run -v <volume: folder shared with docker container> --net='none' <no networking> -m <amount of memory to use> --rm='true'
            #-m not working on Ubuntu: p = subprocess.Popen(['docker', 'run', '--volume', '/root:/test', '--net', 'none', '--rm',
            # '-m', '50m', 'student_test', 'to_test.py', 'test']
            try:
                code_dir = Course.objects.get(name=course_name).student_code_dir + assignment_name.replace(" ","_").encode("ascii", "ignore") + \
                           '/' + request.user.username + '/'
                logging.info(code_dir)
                timeout = Assignment.objects.get(
                    name=assignment_name,
                    course__name=course_name).execution_timeout
                # Dockerfile has to be in the same directory where student code is or student code won't be found!
                subprocess.call(
                    ["cp", student_docker + "Dockerfile", code_dir])
            except Exception as e:
                logging.error(template.format(type(e).__name__, e.args))
                return redirect('error')
            try:
                build_out = open('/var/log/grader/docker_success_student', 'w')
                build_err = open('/var/log/grader/docker_error_student', 'w')

                # TODO: taa kans tappolistalle
                p = subprocess.Popen([
                    'sudo', 'docker', 'build', '-t', 'student_image', code_dir
                ],
                                     stdout=build_out,
                                     stderr=build_err)
                p.communicate()
            except:
                logging.error(
                    'Koodin ajoympäristöä ei voitu käynnistää.'
                    'Jos virhe toistuu, ota yhteyttä kurssihenkilökuntaan')
                if build_out not in locals() or build_err not in locals():
                    build_out.close()
                    build_err.close()
                return redirect('error')
            build_out.close()
            build_err.close()

            out_file = code_dir + 'result.txt'
            err_file = code_dir + 'error.txt'
            # Run student code in Docker: Returns True if running succeeded and False if running took too long
            if run(code_dir, 'student_image', out_file, err_file, timeout):
                # Should have something in either of these
                if not is_empty(out_file):
                    message = read_by_line(out_file)
                else:
                    if not is_empty(err_file):
                        error_message = read_by_line(err_file)
                    else:
                        logging.error(
                            'Both stdout and stderr files were empty')
                        return redirect('error')
            else:
                #TODO: Test this!
                error_message = 'Koodin ajamisessa kesti liian kauan. Ajo keskeytettiin.'
        else:
            logging.error('Form validation error')
    #First time: No POST requests here
    else:
        if not assignment.variable_injection:
            form = EditorForm()
        else:
            form = DoubleEditorForm()

        #Attempt to find student's code from previous visit
        #TODO: Test this!
        code_file = Course.objects.get(name=course_name).student_code_dir + assignment_name.replace(" ","_").encode("ascii", "ignore") +\
                    '/' + request.user.username + '/student_code.py'
        logging.debug("Old code loc " + code_file)
        if os.path.isfile(code_file):
            form.text = read_by_line(code_file)

    return render(
        request, template, {
            "form": form,
            "user": request.user,
            "course": course_name,
            "assignment": assignment,
            "now": datetime.now(),
            "message": message,
            "error": error_message,
            "attempts_left": attempts_left,
        })
Esempio n. 2
0
def code(request):
    #TODO: Can this be moved to a decorator?
    if not 'outcome_url' in request.session or not 'course_name' in request.session \
	    or not 'assignment_name' in request.session:
        #This error message should be as it is, as the site can't load properly without these parameters
        return HttpResponse("Missing parameters")
    
    message = ""
    error_message = ""
    template = "grade/editor.html"

    # Fetch the object matching the assignment name given by the LTI
    assignment_name = request.session['assignment_name']
    course_name = request.session['course_name']

    try:
        assignment = Assignment.objects.get(name=assignment_name, course__name=request.session['course_name'])
    except Assignment.DoesNotExist:
        logging.warning('Assignment not found: ' + assignment_name)
        #This error message should be as it is, as the site can't load properly without this parameter
        return HttpResponse("No assignment found!")

    attempts_left = None
    #If assignment.attempts > 0, then the number of attempts is limited
    if assignment.attempts > 0:
        try:
            user_attempts = UserAttempts.objects.get(user__username=request.user.username, assignment__id=assignment.id)
            attempts_left = user_attempts.attempts
        except UserAttempts.DoesNotExist:
            #User is trying this for the first time, create new db object that connects the user object to amount of
            #attempts the user has left to try this assignment
            to_add = UserAttempts()
            to_add.user = request.user
            to_add.assignment = assignment
            to_add.attempts = assignment.attempts
            attempts_left = assignment.attempts
            to_add.save()

    if request.method == 'POST':
        if assignment.variable_injection == False:
            form = EditorForm(request.POST)
        else:
            form = DoubleEditorForm(request.POST)
            template = "grade/editor_parameter_inject.html"

        if form.is_valid():
            if not assignment.variable_injection:
                save_code(course_name, assignment_name, request.user.username, form.cleaned_data['text'])
            else:
                save_code(course_name, assignment_name, request.user.username, form.cleaned_data['parameters'] +
                          '\n' + form.cleaned_data['text'])
            #docker run -v <volume: folder shared with docker container> --net='none' <no networking> -m <amount of memory to use> --rm='true'
            #-m not working on Ubuntu: p = subprocess.Popen(['docker', 'run', '--volume', '/root:/test', '--net', 'none', '--rm',
            # '-m', '50m', 'student_test', 'to_test.py', 'test']
            try:
                code_dir = Course.objects.get(name=course_name).student_code_dir + assignment_name.replace(" ","_").encode("ascii", "ignore") + \
                           '/' + request.user.username + '/'
                logging.info(code_dir)
                timeout = Assignment.objects.get(name=assignment_name, course__name=course_name).execution_timeout
                # Dockerfile has to be in the same directory where student code is or student code won't be found!
                subprocess.call(["cp", student_docker + "Dockerfile", code_dir])
            except Exception as e:
                logging.error( template.format(type(e).__name__, e.args))
                return redirect('error')
            try:
                build_out = open('/var/log/grader/docker_success_student', 'w')
                build_err = open('/var/log/grader/docker_error_student', 'w')

                # TODO: taa kans tappolistalle
                p = subprocess.Popen(['sudo', 'docker', 'build', '-t', 'student_image', code_dir],
                                     stdout=build_out, stderr=build_err)
                p.communicate()
            except:
                logging.error('Koodin ajoympäristöä ei voitu käynnistää.'
                              'Jos virhe toistuu, ota yhteyttä kurssihenkilökuntaan')
                if build_out not in locals() or build_err not in locals():
                    build_out.close()
                    build_err.close()
                return redirect('error')
            build_out.close()
            build_err.close()

            out_file = code_dir + 'result.txt'
            err_file  = code_dir + 'error.txt'
            # Run student code in Docker: Returns True if running succeeded and False if running took too long
            if run(code_dir, 'student_image', out_file, err_file, timeout):
                # Should have something in either of these
                if not is_empty(out_file):
                    message = read_by_line(out_file)
                else:
                    if not is_empty(err_file):
                        error_message = read_by_line(err_file)
                    else:
                        logging.error('Both stdout and stderr files were empty')
                        return redirect('error')
            else:
                #TODO: Test this!
                error_message = 'Koodin ajamisessa kesti liian kauan. Ajo keskeytettiin.'
        else:
            logging.error('Form validation error')
    #First time: No POST requests here
    else:
        if not assignment.variable_injection:
            form = EditorForm()
        else:
            form = DoubleEditorForm()

        #Attempt to find student's code from previous visit
        #TODO: Test this!
        code_file = Course.objects.get(name=course_name).student_code_dir + assignment_name.replace(" ","_").encode("ascii", "ignore") +\
                    '/' + request.user.username + '/student_code.py'
        logging.debug("Old code loc " + code_file)
        if os.path.isfile(code_file):
            form.text = read_by_line(code_file)

    return render(request, template,
                  {
                    "form": form,
                    "user": request.user,
                    "course": course_name,
                    "assignment": assignment,
                    "now": datetime.now(),
                    "message": message,
                    "error": error_message,
                    "attempts_left": attempts_left,
                  })
Esempio n. 3
0
def grade(request):
    #TODO: Can this be moved to a decorator?
    if not 'outcome_url' in request.session or not 'course_name' in request.session \
            or not 'assignment_name' in request.session:
        #This error message should be as it is, as the site can't load properly without these parameters
        return HttpResponse("Missing parameters")

    if request.method == 'POST':
        form = EditorForm(request.POST)
        message = ""
        error_message = ""
        assignment_name = request.session['assignment_name']
        course_name = request.session['course_name']

        try:
            assignment = Assignment.objects.get(name=assignment_name,
                                                course__name=course_name)
        except Exception as e:
            logging.error(template.format(type(e).__name__, e.args))
            return redirect('error')

        attempts_left = 0
        if assignment.attempts > 0:
            try:
                user_attempts = UserAttempts.objects.get(
                    user__username=request.user.username,
                    assignment__id=assignment.id)
                #One attempt has been used
                attempts_left = user_attempts.attempts
                if attempts_left == 0:
                    return HttpResponse(
                        "Olet jo käyttänyt kaikki yrityskertasi")
            except UserAttempts.DoesNotExist:
                #User is trying this for the first time, create new db object that connects the user object to amount of
                #attempts the user has left to try this assignment
                to_add = UserAttempts()
                to_add.user = request.user
                to_add.assignment = assignment
                to_add.attempts = assignment.attempts
                attempts_left = assignment.attempts
                to_add.save()
            except Exception as e:
                logging.error(template.format(type(e).__name__, e.args))
                return redirect('error')

        if form.is_valid():
            #Save on valid form submission
            save_code(course_name, assignment_name, request.user.username,
                      form.cleaned_data['text'])

            code_dir = Course.objects.get(name=course_name).student_code_dir +\
                       assignment_name.replace(" ","_").encode("ascii", "ignore") + '/'\
                       + request.user.username + '/'
            # Copy test files from assignment directory to the shared volume
            test_dir = Course.objects.get(name=request.session['course_name']).assignment_base_dir\
               + request.session['assignment_name'].replace(" ","_").encode("ascii", "ignore") + '/'
            logging.debug("Test_dir " + test_dir)
            #subprocess.call(["cp", test_dir + "/*", code_dir])
            subprocess.call(["cp", student_docker + "Dockerfile", code_dir])
            subprocess.call(["cp", example_docker + "Dockerfile", test_dir])
            description = imp.load_source('description',
                                          test_dir + 'description.py')

            results = []
            for test_name in description.tests:
                test = imp.load_source('test', test_dir + test_name)
                result = None
                if description.scale == "numeric":
                    result = NumericResult()
                elif description.scale == "pass":
                    result = BinaryResult()
                else:
                    logging.error("Faulty type of grading scale!")
                    return redirect('error')
                result.assignment = assignment
                result.test_name = test.name
                result.description = test.description
                result.user = request.user
                #result.save()

                test_result = None
                if test.type == "compare_output":
                    test_result = diff_test(test, code_dir, test_dir, result)
                    logging.debug(', '.join([
                        ' : '.join((k, str(test_result[k]))) for k in sorted(
                            test_result, key=test_result.get, reverse=True)
                    ]))
                    result.feedback = test.test_results[0]
                elif test.type == "variable_injection":
                    test_result = inject_diff_test(test, code_dir, test_dir,
                                                   result)
                    logging.debug(', '.join([
                        ' : '.join((k, str(test_result[k]))) for k in sorted(
                            test_result, key=test_result.get, reverse=True)
                    ]))
                    result.feedback = test.test_results[0]
                #There was an error on test execution, student will not lose attempts
                if test_result['passed'] == "error":
                    logging.error(test_result['message'])
                    return redirect('error')
                elif test_result['passed'] == "yes":
                    logging.debug("Pass")
                    #If assignment.attempts > 0, then the number of attempts is limited
                    if assignment.attempts > 0:
                        user_attempts.attempts = attempts_left - 1
                        user_attempts.save()

                    if description.scale == "numeric":
                        result.type = "numeric"
                        result.score = test.points
                        result.max_score = test.points
                    elif description.scale == "pass":
                        result.type = "pass"
                        result.passed = True
                #Didn't pass!
                else:
                    logging.debug("Da No Pass")
                    #Assignment attempts were limited
                    if assignment.attempts > 0:
                        user_attempts.attempts = attempts_left - 1
                        user_attempts.save()

                    #Depending on the scale used, give some results
                    if description.scale == "numeric":
                        result.type = "numeric"
                        result.score = 0
                        result.max_score = test.points
                    elif description.scale == "pass":
                        result.type = "pass"
                        result.passed = False

                result.save()
                results.append(result)

        scored_points = 0
        for result in results:
            scored_points += result.score
            logging.debug("Code: " + result.student_result)

        success = False
        if scored_points >= description.points_required:
            success = True

        http = httplib2.Http()
        headers = {'Content-type': 'application/xml'}
        response, content = http.request(request.session['outcome_url'],
                                         'POST',
                                         headers=headers)
        logging.debug(response)
        logging.debug(content)
        return render(
            request, "grade/results.html", {
                "assignment_name": assignment_name,
                "message": message,
                "error": error_message,
                "attempts_left": attempts_left,
                "results": results,
                "scored_points": scored_points,
                "total_points": description.total_points,
                "success": success,
            })
    #Not POST
    else:
        return HttpResponse("Eipäs kurkita!")
Esempio n. 4
0
def grade(request):
    #TODO: Can this be moved to a decorator?
    if not 'outcome_url' in request.session or not 'course_name' in request.session \
            or not 'assignment_name' in request.session:
        #This error message should be as it is, as the site can't load properly without these parameters
        return HttpResponse("Missing parameters")

    if request.method == 'POST':
        form = EditorForm(request.POST)
        message = ""
        error_message = ""
        assignment_name = request.session['assignment_name']
        course_name = request.session['course_name']

        try:
            assignment = Assignment.objects.get(name=assignment_name, course__name=course_name)
        except Exception as e:
            logging.error(template.format(type(e).__name__, e.args))
            return redirect('error')

        attempts_left = 0	
        if assignment.attempts > 0:
            try:
                user_attempts = UserAttempts.objects.get(user__username=request.user.username,
                                                         assignment__id=assignment.id)
                #One attempt has been used
                attempts_left = user_attempts.attempts
                if attempts_left == 0:
                    return HttpResponse("Olet jo käyttänyt kaikki yrityskertasi")
            except UserAttempts.DoesNotExist:
                #User is trying this for the first time, create new db object that connects the user object to amount of
                #attempts the user has left to try this assignment
                to_add = UserAttempts()
                to_add.user = request.user
                to_add.assignment = assignment
                to_add.attempts = assignment.attempts
                attempts_left = assignment.attempts
                to_add.save()
            except Exception as e:
                logging.error(template.format(type(e).__name__, e.args))
                return redirect('error')

        if form.is_valid():
            #Save on valid form submission
            save_code(course_name, assignment_name, request.user.username, form.cleaned_data['text'])

            code_dir = Course.objects.get(name=course_name).student_code_dir +\
                       assignment_name.replace(" ","_").encode("ascii", "ignore") + '/'\
                       + request.user.username + '/'
            # Copy test files from assignment directory to the shared volume
            test_dir = Course.objects.get(name=request.session['course_name']).assignment_base_dir\
               + request.session['assignment_name'].replace(" ","_").encode("ascii", "ignore") + '/'
            logging.debug("Test_dir " + test_dir)
            #subprocess.call(["cp", test_dir + "/*", code_dir])
            subprocess.call(["cp", student_docker + "Dockerfile", code_dir])
            subprocess.call(["cp", example_docker + "Dockerfile", test_dir])
            description = imp.load_source('description', test_dir + 'description.py')

            results = []
            for test_name in description.tests:
                test = imp.load_source('test', test_dir + test_name)
                result = None
                if description.scale == "numeric":
                    result = NumericResult()
                elif description.scale == "pass":
                    result = BinaryResult()
                else:
                    logging.error("Faulty type of grading scale!")
                    return redirect('error')
                result.assignment = assignment
                result.test_name = test.name
                result.description = test.description
                result.user = request.user
                #result.save()

                test_result = None
                if test.type == "compare_output":
                    test_result = diff_test(test, code_dir, test_dir, result)
                    logging.debug(', '.join([' : '.join(
                        (k, str(test_result[k]))) for k in sorted(test_result, key=test_result. get, reverse=True)]))
                    result.feedback = test.test_results[0]
                elif test.type == "variable_injection":
                    test_result = inject_diff_test(test, code_dir, test_dir, result)
                    logging.debug(', '.join([' : '.join(
                        (k, str(test_result[k]))) for k in sorted(test_result, key=test_result. get, reverse=True)]))
                    result.feedback = test.test_results[0]
                #There was an error on test execution, student will not lose attempts
                if test_result['passed'] == "error":
                    logging.error(test_result['message'])
                    return redirect('error')
                elif test_result['passed'] == "yes":
                    logging.debug("Pass")
                    #If assignment.attempts > 0, then the number of attempts is limited
                    if assignment.attempts > 0:
                        user_attempts.attempts = attempts_left - 1
                        user_attempts.save()

                    if description.scale == "numeric":
                        result.type = "numeric"
                        result.score = test.points
                        result.max_score = test.points
                    elif description.scale == "pass":
                        result.type = "pass"
                        result.passed = True
                #Didn't pass!
                else:
                    logging.debug("Da No Pass")
                    #Assignment attempts were limited
                    if assignment.attempts > 0:
                        user_attempts.attempts = attempts_left - 1
                        user_attempts.save()

                    #Depending on the scale used, give some results
                    if description.scale == "numeric":
                        result.type = "numeric"
                        result.score = 0
                        result.max_score = test.points
                    elif description.scale == "pass":
                        result.type = "pass"
                        result.passed = False

                result.save()
                results.append(result)

        scored_points = 0
        for result in results:
            scored_points += result.score
            logging.debug("Code: " + result.student_result)

        success = False
        if scored_points >= description.points_required:
            success = True

        http = httplib2.Http()
        headers = {'Content-type': 'application/xml'}
        response, content = http.request(request.session['outcome_url'], 'POST', headers=headers)
        logging.debug(response)
        logging.debug(content)
        return render(request, "grade/results.html",
        {
            "assignment_name": assignment_name,
            "message": message,
            "error": error_message,
            "attempts_left": attempts_left,
            "results": results,
            "scored_points": scored_points,
            "total_points": description.total_points,
            "success": success,
        })
    #Not POST
    else:
        return HttpResponse("Eipäs kurkita!")