Exemple #1
0
    def test_score_staff(self):
        self._test_backup(True)

        user = User.lookup(self.user1.email)
        self.login(self.staff1.email)

        response = self.client.post('/api/v3/score/')
        self.assert_400(response)
        assert response.json['code'] == 400
        backup = Backup.query.filter(Backup.submitter_id == user.id).first()

        data = {'bid': encode_id(backup.id), 'kind': 'Total',
                'score': 128.2, 'message': 'wow'}
        response = self.client.post('/api/v3/score/', data=data)
        self.assert_200(response)
        assert response.json['code'] == 200

        self.logout()
        self.login(self.admin.email)

        data = {'bid': encode_id(backup.id), 'kind': 'Total',
                'score': 128.2, 'message': 'wow'}
        response = self.client.post('/api/v3/score/', data=data)
        self.assert_200(response)
        assert response.json['code'] == 200
Exemple #2
0
    def test_score_staff(self):
        self._test_backup(True)

        user = User.lookup(self.user1.email)
        self.login(self.staff1.email)

        response = self.client.post('/api/v3/score/')
        self.assert_400(response)
        assert response.json['code'] == 400
        backup = Backup.query.filter(Backup.submitter_id == user.id).first()

        data = {
            'bid': encode_id(backup.id),
            'kind': 'Total',
            'score': 128.2,
            'message': 'wow'
        }
        response = self.client.post('/api/v3/score/', data=data)
        self.assert_200(response)
        assert response.json['code'] == 200

        self.logout()
        self.login(self.admin.email)

        data = {
            'bid': encode_id(backup.id),
            'kind': 'Total',
            'score': 128.2,
            'message': 'wow'
        }
        response = self.client.post('/api/v3/score/', data=data)
        self.assert_200(response)
        assert response.json['code'] == 200
Exemple #3
0
def autograde_assignment(assignment, ag_assign_key, token, autopromotion=True):
    """ Autograde all enrolled students for this assignment.
    If ag_assign_key is 'test', the autograder will respond with 'OK' but not grade.

    @assignment: Assignment object.
    @ag_assign_key: Autograder ID (from Autograder Dashboard)
    @token: OK Access Token (from auth)
    """
    students, submissions, no_submissions = assignment.course_submissions()

    backups_to_grade = [utils.encode_id(bid) for bid in submissions]

    if autopromotion:
        # Hunt for backups from those with no_submissions
        seen = set()
        for student_uid in no_submissions:
            if student_uid not in seen:
                found_backup = assignment.backups([student_uid]).first()
                if found_backup:
                    seen |= found_backup.owners()
                    backups_to_grade.append(utils.encode_id(found_backup.id))
                    found_backup.submit = True
        db.session.commit()

    data = {
        'subm_ids': backups_to_grade,
        'assignment': ag_assign_key,
        'access_token': token,
        'priority': 'default',
        'backup_url': url_for('api.backup', _external=True),
        'ok-server-version': 'v3',
        'testing': token == 'testing',
    }
    return send_autograder('/api/ok/v3/grade/batch', data)
Exemple #4
0
 def test_hashids(self):
     """Tests converting hashes in URLs to IDs. Do not change the values in
     this test.
     """
     assert utils.encode_id(314159) == 'aAPZ9j'
     assert utils.decode_id('aAPZ9j') == 314159
     assert utils.encode_id(11235) == 'b28KJe'
     assert utils.decode_id('b28KJe') == 11235
     self.assertRaises(ValueError, utils.decode_id, 'deadbeef')
Exemple #5
0
 def test_hashids(self):
     """Tests converting hashes in URLs to IDs. Do not change the values in
     this test.
     """
     assert utils.encode_id(314159) == 'aAPZ9j'
     assert utils.decode_id('aAPZ9j') == 314159
     assert utils.encode_id(11235) == 'b28KJe'
     assert utils.decode_id('b28KJe') == 11235
     self.assertRaises(ValueError, utils.decode_id, 'deadbeef')
Exemple #6
0
 def test_hashids(self):
     """Tests converting hashes in URLs to IDs. Do not change the values in
     this test.
     """
     assert self.app.url_map.converters['hashid'] == utils.HashidConverter
     assert utils.encode_id(314159) == 'aAPZ9j'
     assert utils.decode_id('aAPZ9j') == 314159
     assert utils.encode_id(11235) == 'b28KJe'
     assert utils.decode_id('b28KJe') == 11235
     self.assertRaises(ValidationError, utils.decode_id, 'deadbeef')
Exemple #7
0
    def post(self, user, key=None):
        if key is not None:
            restful.abort(405)
        try:
            backup = self.schema.store_backup(user)
        except ValueError as e:
            data = {'backup': True}
            if 'late' in str(e).lower():
                data['late'] = True
            return restful.abort(403, message=str(e), data=data)

        assignment = backup.assignment

        # Only accept revision if the assignment has revisions enabled
        if not assignment.revisions_allowed:
            return restful.abort(403,
                                 message=("Revisions are not enabled for {}"
                                          .format(assignment.name)),
                                 data={'backup': True, 'late': True})

        # Only accept revision if the user has a FS
        group = assignment.active_user_ids(user.id)
        fs = assignment.final_submission(group)

        if not fs:
            return restful.abort(403, message="No Submission to Revise", data={})

        # Get previous revision, (There should only be one)
        previous_revision = assignment.revision(group)
        if previous_revision:
            for score in previous_revision.scores:
                if score.kind == "revision":
                    score.archive()
        models.db.session.commit()
        fs_url = url_for('student.code', name=assignment.name, submit=fs.submit,
                         bid=encode_id(fs.id), _external=True)

        assignment_creator = models.User.get_by_id(assignment.creator_id)

        make_score(assignment_creator, backup, 2.0, "Revision for {}".format(fs_url),
                   "revision")
        backup_url = url_for('student.code', name=assignment.name, submit=backup.submit,
                             bid=encode_id(backup.id), _external=True)

        return {
            'email': current_user.email,
            'key': encode_id(backup.id),
            'url': backup_url,
            'course': assignment.course,
            'assignment': assignment.name,
        }
Exemple #8
0
    def post(self, user, key=None):
        if key is not None:
            restful.abort(405)
        try:
            backup = self.schema.store_backup(user)
        except ValueError as e:
            data = {'backup': True}
            if 'late' in str(e).lower():
                data['late'] = True
            return restful.abort(403, message=str(e), data=data)

        assignment = backup.assignment

        # Only accept revision if the assignment has revisions enabled
        if not assignment.revisions_allowed:
            return restful.abort(403,
                                 message="Revisions are not enabled for this assignment",
                                 data={'backup': True, 'late': True})

        # Only accept revision if the user has a FS
        group = assignment.active_user_ids(user.id)
        fs = assignment.final_submission(group)

        if not fs:
            return restful.abort(403, message="No Submission to Revise", data={})

        # Get previous revision, (There should only be one)
        previous_revision = assignment.revision(group)
        if previous_revision:
            for score in previous_revision.scores:
                if score.kind == "revision":
                    score.archive()
        models.db.session.commit()
        fs_url = url_for('student.code', name=assignment.name, submit=fs.submit,
                         bid=encode_id(fs.id), _external=True)

        assignment_creator = models.User.get_by_id(assignment.creator_id)

        make_score(assignment_creator, backup, 0.0, "Revision for {}".format(fs_url),
                   "revision")
        backup_url = url_for('student.code', name=assignment.name, submit=backup.submit,
                             bid=encode_id(backup.id), _external=True)

        return {
            'email': current_user.email,
            'key': encode_id(backup.id),
            'url': backup_url,
            'course': assignment.course,
            'assignment': assignment.name,
        }
Exemple #9
0
 def test_edit_extension(self):
     ext = self._make_ext(self.assignment, self.user1)
     self.login(self.staff1.email)
     expires = (dt.datetime.utcnow() + dt.timedelta(days=3)).strftime(
         constants.ISO_DATETIME_FMT)
     custom_submission_time = dt.datetime.utcnow().strftime(
         constants.ISO_DATETIME_FMT)
     message = 'Sickness'
     data = {
         'assignment_id': self.assignment.id,
         'email': self.user1.email,
         'expires': expires,
         'reason': message,
         'submission_time': 'other',
         'custom_submission_time': custom_submission_time
     }
     self.assert200(
         self.client.post('/admin/course/{}/extensions/{}'.format(
             self.course.id, utils.encode_id(ext.id)),
                          data=data,
                          follow_redirects=True))
     extension = Extension.get_extension(self.user1,
                                         self.assignment,
                                         time=dt.datetime.utcnow())
     self.assertEqual(extension.staff_id, self.staff1.id)
     self.assertEqual(extension.assignment_id, self.assignment.id)
     self.assertEqual(extension.message, message)
     self.assertEqual(
         utils.local_time_obj(extension.expires,
                              self.course).replace(tzinfo=None),
         dt.datetime.strptime(expires, '%Y-%m-%d %H:%M:%S'))
     self.assertEqual(
         utils.local_time_obj(extension.custom_submission_time,
                              self.course).replace(tzinfo=None),
         dt.datetime.strptime(custom_submission_time, '%Y-%m-%d %H:%M:%S'))
Exemple #10
0
 def test_binary_download(self):
     self.login(self.staff1.email)
     encoded_id = utils.encode_id(self.file2.id)
     url = "/files/{0}".format(encoded_id)
     headers, data = self.fetch_file(url)
     self.verify_download_headers(headers, self.file2.filename, "image/svg+xml")
     self.verify_binary_download(CWD + "/../server/static/img/logo.svg", data)
Exemple #11
0
 def test_api_download(self):
     self.login(self.staff1.email)
     encoded_id = utils.encode_id(self.file1.id)
     url = "/api/v3/file/{0}".format(encoded_id)
     headers, data = self.fetch_file(url)
     self.verify_download_headers(headers, self.file1.filename, "text/plain; charset=utf-8")
     self.verify_text_download(CWD + "/files/fizzbuzz_after.py", data)
Exemple #12
0
    def test_comment_staff(self):
        self._test_backup(True)

        user = User.lookup(self.user1.email)
        self.login(self.staff1.email)
        backup = Backup.query.filter(Backup.submitter_id == user.id).first()
        comment_url = "/api/v3/backups/{}/comment/".format(encode_id(
            backup.id))

        response = self.client.post(comment_url)
        self.assert_400(response)  # Not all fields present
        assert response.json['code'] == 400

        data = {'line': 2, 'filename': 'fizzbuzz.py', 'message': 'wow'}
        response = self.client.post(comment_url, data=data)
        self.assert_200(response)
        assert response.json['code'] == 200

        self.logout()
        self.login(self.admin.email)

        data = {'line': 2, 'filename': 'fizzbuzz.py', 'message': 'wow'}
        response = self.client.post(comment_url, data=data)
        self.assert_200(response)
        assert response.json['code'] == 200

        # Check that another student is not able to comment
        self.login(self.user2.email)
        data = {'line': 2, 'filename': 'fizzbuzz.py', 'message': 'wow'}
        response = self.client.post(comment_url, data=data)
        self.assert_403(response)
        assert response.json['code'] == 403
Exemple #13
0
    def post(self, user, key=None):
        if key is not None:
            restful.abort(405)
        try:
            backup = self.schema.store_backup(user)
        except ValueError as e:
            data = {'backup': True}
            if 'late' in str(e).lower():
                data['late'] = True
            return restful.abort(403, message=str(e), data=data)

        assignment = backup.assignment
        return {
            'email':
            current_user.email,
            'key':
            encode_id(backup.id),
            'url':
            url_for('student.code',
                    name=assignment.name,
                    submit=backup.submit,
                    bid=backup.id,
                    _external=True),
            'course':
            assignment.course,
            'assignment':
            assignment.name
        }
Exemple #14
0
 def test_api_download(self):
     self.login(self.staff1.email)
     encoded_id = utils.encode_id(self.file1.id)
     url = "/api/v3/file/{0}".format(encoded_id)
     headers, data = self.fetch_file(url)
     self.verify_download_headers(headers, self.file1.filename,
                                  "text/plain; charset=utf-8")
     self.verify_text_download(CWD + "/files/fizzbuzz_after.py", data)
Exemple #15
0
def upload_scores(canvas_assignment_id):
    logger = jobs.get_job_logger()
    canvas_assignment = CanvasAssignment.query.get(canvas_assignment_id)
    canvas_course = canvas_assignment.canvas_course
    assignment = canvas_assignment.assignment
    course = assignment.course

    logger.info('Starting bCourses upload')
    logger.info('bCourses assignment URL: {}'.format(canvas_assignment.url))
    logger.info('OK assignment: {}'.format(assignment.display_name))
    logger.info('Scores: {}'.format(', '.join(canvas_assignment.score_kinds)))

    students = api.get_students(canvas_course)
    old_scores = api.get_scores(canvas_assignment)
    new_scores = {}
    stats = collections.Counter()

    row_format = '{!s:>10}  {!s:<55}  {!s:<6}  {!s:>9}  {!s:>9}'
    logger.info(row_format.format('STUDENT ID', 'EMAIL', 'BACKUP', 'OLD SCORE', 'NEW SCORE'))

    for student in students:
        canvas_user_id = student['id']
        sid = student['sis_user_id']
        enrollments = Enrollment.query.filter_by(
            course_id=canvas_course.course_id,
            sid=sid,
            role=constants.STUDENT_ROLE,
        ).all()
        emails = ','.join(enrollment.user.email for enrollment in enrollments) or 'None'
        scores = []
        for enrollment in enrollments:
            user_ids = assignment.active_user_ids(enrollment.user_id)
            scores.extend(assignment.scores(user_ids))
        scores = [s for s in scores if s.kind in canvas_assignment.score_kinds]
        old_score = old_scores.get(canvas_user_id)
        if not scores:
            new_score = None
            backup_id = None
            stats['no_scores'] += 1
        else:
            max_score = max(scores, key=lambda score: score.score)
            new_score = max_score.score
            backup_id = encode_id(max_score.backup_id)
            if old_score != new_score:
                new_scores[canvas_user_id] = new_score
                stats['updated'] += 1
            else:
                stats['not_changed'] += 1
        logger.info(row_format.format(sid, emails, backup_id, old_score, new_score))

    if new_scores:
        api.put_scores(canvas_assignment, new_scores)

    stats = ('{updated} updated, {not_changed} not changed, '
             '{no_scores} no scores'.format(**stats))
    logger.info(stats)
    return stats
Exemple #16
0
def test_job(duration=0, should_fail=False, make_file=False):
    logger = jobs.get_job_logger()

    logger.info('Starting...')
    time.sleep(duration)
    if should_fail:
        1/0
    if make_file:
        upload = ExternalFile.upload(data(duration+1), user_id=1, course_id=1,
                                     name='temp.okfile', prefix='jobs/example/')
        logger.info("Saved as: {}".format(upload.object_name))
        logger.info('File ID: {0}'.format(encode_id(upload.id)))
        msg = ("Waited for <a href='/files/{0}'> {1} seconds </a>"
               .format(encode_id(upload.id), duration))
    else:
        msg = "Waited for <b>{}</b> seconds!".format(duration)
    logger.info('Finished!')
    return msg
Exemple #17
0
 def retry_task(task):
     if task.retries >= MAX_RETRIES:
         logger.error('Did not receive a score for backup {} after {} retries'.format(
             utils.encode_id(task.backup_id), MAX_RETRIES))
         task.set_status(GradingStatus.FAILED)
     else:
         task.set_status(GradingStatus.QUEUED)
         task.job_id = autograde_backup(token, assignment, task.backup_id)
         task.retries += 1
Exemple #18
0
 def test_binary_download(self):
     self.login(self.staff1.email)
     encoded_id = utils.encode_id(self.file2.id)
     url = "/files/{0}".format(encoded_id)
     headers, data = self.fetch_file(url)
     self.verify_download_headers(headers, self.file2.filename,
                                  "image/svg+xml; charset=utf-8")
     self.verify_binary_download(CWD + "/../server/static/img/logo.svg",
                                 data)
 def test_binary_download(self):
     self.login(self.staff1.email)
     encoded_id = utils.encode_id(self.file2.id)
     url = "/files/{0}".format(encoded_id)
     response = self.client.get(url)
     self.assert200(response)
     self.assertEquals("attachment; filename={0!s}".format(self.file2.filename),
                       response.headers.get('Content-Disposition'))
     self.assertEquals(response.headers['Content-Type'], 'image/svg+xml')
     self.assertEquals(response.headers['X-Content-Type-Options'], 'nosniff')
Exemple #20
0
 def test_binary_api_download(self):
     self.login(self.staff1.email)
     encoded_id = utils.encode_id(self.file2.id)
     url = "/api/v3/file/{0}".format(encoded_id)
     self.assertEqual(self.file2.download_link, url)
     headers, data = self.fetch_file(url)
     self.verify_download_headers(headers, self.file2.filename,
                                  "image/svg+xml")
     self.verify_binary_download(CWD + "/../server/static/img/logo.svg",
                                 data)
Exemple #21
0
 def test_folders(self):
     filename = "tests/hof.py"
     contents = "tests = {\nstatus: 'locked'\n}"
     self._add_file(filename, contents)
     encoded_id = utils.encode_id(self.backup.id)
     submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}".format(self.assignment.name, submit_str, encoded_id, filename)
     response = self.client.get(url)
     self.assert_200(response)
     self.assertEqual(contents, response.data.decode('UTF-8'))
Exemple #22
0
 def retry_task(task):
     if task.retries >= MAX_RETRIES:
         logger.error(
             'Did not receive a score for backup {} after {} retries'.
             format(utils.encode_id(task.backup_id), MAX_RETRIES))
         task.set_status(GradingStatus.FAILED)
     else:
         task.set_status(GradingStatus.QUEUED)
         task.job_id = autograde_backup(token, assignment, task.backup_id)
         task.retries += 1
Exemple #23
0
 def test_wrong_student(self):
     filename = "test.py"
     contents = "x = 4"
     self._add_file(filename, contents)
     self.login('*****@*****.**')
     encoded_id = utils.encode_id(self.backup.id)
     submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}".format(self.assignment.name, submit_str, encoded_id, filename)
     response = self.client.get(url)
     self.assert_404(response)
Exemple #24
0
    def test_get_backup(self):
        self._test_backup(False)

        backup = Backup.query.first()
        submission_time = (self.assignment.due_date
            - dt.timedelta(days=random.randrange(0, 10)))
        backup.custom_submission_time = submission_time

        response = self.client.get('/api/v3/backups/{}/'.format(backup.hashid))
        self.assert_200(response)

        course = backup.assignment.course
        user_json = {
            "email": backup.submitter.email,
            "id": encode_id(backup.submitter_id),
        }
        response_json = response.json['data']
        time_threshold = dt.timedelta(seconds=5)
        self.assertAlmostEqual(dateutil.parser.parse(response_json['created']),
                               backup.created,
                               delta=time_threshold)
        self.assertAlmostEqual(dateutil.parser.parse(response_json['submission_time']),
                               submission_time,
                               delta=time_threshold)
        self.assertAlmostEqual(dateutil.parser.parse(response_json['messages'][0]['created']),
                               backup.created,
                               delta=time_threshold)
        # Unset timestamps already tested.
        del response_json['created']
        del response_json['submission_time']
        del response_json['messages'][0]['created']
        assert response_json == {
            "submitter": user_json,
            "submit": backup.submit,
            "group": [user_json],
            "is_late": backup.is_late,
            "external_files": [],
            "assignment": {
                "name": backup.assignment.name,
                "course": {
                    "id": course.id,
                    "active": course.active,
                    "display_name": course.display_name,
                    "offering": course.offering,
                    "timezone": course.timezone.zone,
                },
            },
            "id": backup.hashid,
            "messages": [
                {
                    "kind": "file_contents",
                    "contents": backup.files(),
                },
            ],
        }
Exemple #25
0
    def post(self, user, key=None):
        if key is not None:
            restful.abort(405)
        try:
            backup = self.schema.store_backup(user)
        except ValueError as e:
            data = {'backup': True}
            if 'late' in str(e).lower():
                data['late'] = True
            return restful.abort(403, message=str(e), data=data)

        assignment = backup.assignment
        return {
            'email': current_user.email,
            'key': encode_id(backup.id),
            'url': url_for('student.code', name=assignment.name, submit=backup.submit,
                           bid=encode_id(backup.id), _external=True),
            'course': assignment.course,
            'assignment': assignment.name
        }
Exemple #26
0
    def test_unauth_download(self):
        self.login(self.user1.email)
        encoded_id = utils.encode_id(self.file1.id)
        url = "/files/{0}".format(encoded_id)
        response = self.client.get(url)
        self.assert404(response)

        # Should also fail via the API
        url = "/api/v3/file/{0}".format(encoded_id)
        response = self.client.get(url)
        self.assert404(response)
Exemple #27
0
def upload_scores(canvas_assignment_id):
    logger = jobs.get_job_logger()
    canvas_assignment = CanvasAssignment.query.get(canvas_assignment_id)
    canvas_course = canvas_assignment.canvas_course
    assignment = canvas_assignment.assignment
    course = assignment.course

    logger.info('Starting bCourses upload')
    logger.info('bCourses assignment URL: {}'.format(canvas_assignment.url))
    logger.info('OK assignment: {}'.format(assignment.display_name))
    logger.info('Scores: {}'.format(', '.join(canvas_assignment.score_kinds)))

    students = api.get_students(canvas_course)
    old_scores = api.get_scores(canvas_assignment)
    new_scores = {}
    stats = collections.Counter()

    row_format = '{!s:>10}  {!s:<55}  {!s:<6}  {!s:>9}  {!s:>9}'
    logger.info(row_format.format('STUDENT ID', 'EMAIL', 'BACKUP', 'OLD SCORE', 'NEW SCORE'))

    for student in students:
        canvas_user_id = student['id']
        sid = student['sis_user_id']
        enrollments = Enrollment.query.filter_by(
            course_id=canvas_course.course_id,
            sid=sid,
            role=constants.STUDENT_ROLE,
        ).all()
        emails = ','.join(enrollment.user.email for enrollment in enrollments) or 'None'
        scores = []
        for enrollment in enrollments:
            user_ids = assignment.active_user_ids(enrollment.user_id)
            scores.extend(assignment.scores(user_ids))
        scores = [s for s in scores if s.kind in canvas_assignment.score_kinds]
        old_score = old_scores.get(canvas_user_id)
        if not scores:
            new_score = None
            backup_id = None
            stats['no_scores'] += 1
        else:
            max_score = max(scores, key=lambda score: score.score)
            new_score = max_score.score
            backup_id = encode_id(max_score.backup_id)
            if old_score != new_score:
                new_scores[canvas_user_id] = new_score
                stats['updated'] += 1
            else:
                stats['not_changed'] += 1
        logger.info(row_format.format(sid, emails, backup_id, old_score, new_score))

    api.put_scores(canvas_assignment, new_scores)

    logger.info('{updated} updated, {not_changed} not changed, '
        '{no_scores} no scores'.format(**stats))
Exemple #28
0
    def test_unauth_download(self):
        self.login(self.user1.email)
        encoded_id = utils.encode_id(self.file1.id)
        url = "/files/{0}".format(encoded_id)
        response = self.client.get(url)
        self.assert404(response)

        # Should also fail via the API
        url = "/api/v3/file/{0}".format(encoded_id)
        response = self.client.get(url)
        self.assert404(response)
 def test_api_download(self):
     self.login(self.staff1.email)
     encoded_id = utils.encode_id(self.file1.id)
     url = "/api/v3/file/{0}".format(encoded_id)
     response = self.client.get(url)
     self.assert200(response)
     self.assertEquals("attachment; filename={0!s}".format(self.file1.filename),
                       response.headers.get('Content-Disposition'))
     self.assertEquals(response.headers['Content-Type'], 'text/plain; charset=utf-8')
     self.assertEquals(response.headers['X-Content-Type-Options'], 'nosniff')
     with open(CWD + "/files/fizzbuzz_after.py", 'r') as f:
         self.assertEqual(f.read(), response.data.decode('UTF-8'))
Exemple #30
0
def grade_single(backup, ag_assign_key, token):

    data = {
        'subm_ids': [utils.encode_id(backup.id)],
        'assignment': ag_assign_key,
        'access_token': token,
        'priority': 'default',
        'backup_url': url_for('api.backup', _external=True),
        'ok-server-version': 'v3',
        'testing': token == 'testing',
    }
    return send_autograder('/api/ok/v3/grade/batch', data)
Exemple #31
0
 def test_incorrect_hash(self):
     filename = "test.py"
     contents = "x = 4"
     self._add_file(filename, contents)
     encoded_id = utils.encode_id(self.backup.id)
     submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}".format(self.assignment.name, submit_str, "xxxxx", filename)
     response = self.client.get(url)
     self.assert_404(response)
     url = "/{0}/{1}/{2}/download/{3}".format(self.assignment.name, submit_str, "123", filename)
     response = self.client.get(url)
     self.assert_404(response)
Exemple #32
0
def test_job(duration=0, should_fail=False, make_file=False):
    logger = jobs.get_job_logger()

    logger.info('Starting...')
    time.sleep(duration)
    if should_fail:
        1 / 0
    if make_file:
        upload = ExternalFile.upload(data(duration + 1),
                                     user_id=1,
                                     course_id=1,
                                     name='temp.okfile',
                                     prefix='jobs/example/')
        logger.info("Saved as: {}".format(upload.object_name))
        logger.info('File ID: {0}'.format(encode_id(upload.id)))
        msg = ("Waited for <a href='/files/{0}'> {1} seconds </a>".format(
            encode_id(upload.id), duration))
    else:
        msg = "Waited for <b>{}</b> seconds!".format(duration)
    logger.info('Finished!')
    return msg
Exemple #33
0
 def test_wrong_student(self):
     filename = "test.py"
     contents = "x = 4"
     self._add_file(filename, contents)
     self.login('*****@*****.**')
     encoded_id = utils.encode_id(self.backup.id)
     submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}".format(self.assignment.name,
                                              submit_str, encoded_id,
                                              filename)
     response = self.client.get(url)
     self.assert_404(response)
Exemple #34
0
 def test_unicode(self):
     filename = "test.py"
     contents = "⚡️ 🔥 💥 ❄️"
     self._add_file(filename, contents)
     encoded_id = utils.encode_id(self.backup.id)
     submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}".format(self.assignment.name,
                                              submit_str, encoded_id,
                                              filename)
     response = self.client.get(url)
     self.assert_200(response)
     self.assertEqual(contents, response.data.decode('UTF-8'))
Exemple #35
0
 def test_raw(self):
     filename = "test.py"
     contents = "x = 4"
     self._add_file(filename, contents)
     encoded_id = utils.encode_id(self.backup.id)
     submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}?raw=1".format(self.assignment.name, submit_str, encoded_id, filename)
     response = self.client.get(url)
     self.assert_200(response)
     self.assertTrue('inline' in response.headers['Content-Disposition'])
     self.assertEquals(response.headers['Content-Type'], 'text/plain; charset=UTF-8')
     self.assertEquals(response.headers['X-Content-Type-Options'], 'nosniff')
     self.assertEqual(contents, response.data.decode('UTF-8'))
Exemple #36
0
 def test_incorrect_submit_boolean(self):
     filename = "test.py"
     contents = "x = 4"
     self._add_file(filename, contents)
     encoded_id = utils.encode_id(self.backup.id)
     wrong_submit_str = "backups" if self.backup.submit else "submissions" # intentionally flipped
     correct_submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}"
     wrong_url = url.format(self.assignment.name, wrong_submit_str, encoded_id, filename)
     redir_url = url.format(self.assignment.name, correct_submit_str, encoded_id, filename)
     response = self.client.get(wrong_url)
     self.assertRedirects(response, redir_url)
     response = self.client.get(redir_url)
     self.assertEqual(contents, response.data.decode('UTF-8'))
Exemple #37
0
        def test_admin_student_assign_diff_overview(self):
            self._login(role="admin")

            backups = []
            for _ in range(3):
                backup = self._gen_backup(self.user1, self.assignment, "hello{}".format(_), _)
                backups.append(backup)
                models.db.session.add(backup)
            models.db.session.commit()
            bid = utils.encode_id(backups[0].id)

            self.page_load(self.get_server_url() + "/admin/course/1/{}/{}/{}?student_email={}".format(
                self.user1.email, self.assignment.id, bid, self.user1.email))
            self.assertIn('Diff Overview', self.driver.title)
Exemple #38
0
 def test_incorrect_hash(self):
     filename = "test.py"
     contents = "x = 4"
     self._add_file(filename, contents)
     encoded_id = utils.encode_id(self.backup.id)
     submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}".format(self.assignment.name,
                                              submit_str, "xxxxx", filename)
     response = self.client.get(url)
     self.assert_404(response)
     url = "/{0}/{1}/{2}/download/{3}".format(self.assignment.name,
                                              submit_str, "123", filename)
     response = self.client.get(url)
     self.assert_404(response)
    def test_get_backup(self):
        self._test_backup(False)

        backup = Backup.query.first()
        submission_time = (self.assignment.due_date -
                           dt.timedelta(days=random.randrange(0, 10)))
        backup.custom_submission_time = submission_time

        response = self.client.get('/api/v3/backups/{}/'.format(backup.hashid))
        self.assert_200(response)

        course = backup.assignment.course
        user_json = {
            "email": backup.submitter.email,
            "id": encode_id(backup.submitter_id),
        }
        assert response.json['data'] == {
            "submitter":
            user_json,
            "submit":
            backup.submit,
            "created":
            backup.created.isoformat(),
            "submission_time":
            submission_time.isoformat(),
            "group": [user_json],
            "is_late":
            backup.is_late,
            "external_files": [],
            "assignment": {
                "name": backup.assignment.name,
                "course": {
                    "id": course.id,
                    "active": course.active,
                    "display_name": course.display_name,
                    "offering": course.offering,
                    "timezone": course.timezone.zone,
                },
            },
            "id":
            backup.hashid,
            "messages": [
                {
                    "kind": "file_contents",
                    "contents": backup.files(),
                    "created": backup.created.isoformat(),
                },
            ],
        }
Exemple #40
0
def export_grades():
    logger = jobs.get_job_logger()
    current_user = jobs.get_current_job().user
    course = Course.query.get(jobs.get_current_job().course_id)
    assignments = course.assignments
    students = (Enrollment.query.options(db.joinedload('user')).filter(
        Enrollment.role == STUDENT_ROLE, Enrollment.course == course).all())

    headers, assignments = get_headers(assignments)
    logger.info("Using these headers:")
    for header in headers:
        logger.info('\t' + header)
    logger.info('')

    total_students = len(students)

    users = [student.user for student in students]
    user_ids = [user.id for user in users]

    all_scores = collect_all_scores(assignments, user_ids)

    with io.StringIO() as f:
        writer = csv.writer(f)
        writer.writerow(headers)  # write headers

        for i, student in enumerate(students, start=1):
            row = export_student_grades(student, assignments, all_scores)
            writer.writerow(row)
            if i % 50 == 0:
                logger.info('Exported {}/{}'.format(i, total_students))
        f.seek(0)
        created_time = local_time(dt.datetime.now(),
                                  course,
                                  fmt='%b-%-d %Y at %I-%M%p')
        csv_filename = '{course_name} Grades ({date}).csv'.format(
            course_name=course.display_name, date=created_time)
        # convert to bytes for csv upload
        csv_bytes = io.BytesIO(bytearray(f.read(), 'utf-8'))
        upload = ExternalFile.upload(csv_bytes,
                                     user_id=current_user.id,
                                     name=csv_filename,
                                     course_id=course.id,
                                     prefix='jobs/exports/{}/'.format(
                                         course.offering))

    logger.info('\nDone!\n')
    logger.info("Saved as: {0}".format(upload.object_name))
    return "/files/{0}".format(encode_id(upload.id))
Exemple #41
0
 def test_raw(self):
     filename = "test.py"
     contents = "x = 4"
     self._add_file(filename, contents)
     encoded_id = utils.encode_id(self.backup.id)
     submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}?raw=1".format(self.assignment.name,
                                                    submit_str, encoded_id,
                                                    filename)
     response = self.client.get(url)
     self.assert_200(response)
     self.assertTrue('inline' in response.headers['Content-Disposition'])
     self.assertEqual(response.headers['Content-Type'],
                      'text/plain; charset=UTF-8')
     self.assertEqual(response.headers['X-Content-Type-Options'], 'nosniff')
     self.assertEqual(contents, response.data.decode('UTF-8'))
Exemple #42
0
 def test_incorrect_submit_boolean(self):
     filename = "test.py"
     contents = "x = 4"
     self._add_file(filename, contents)
     encoded_id = utils.encode_id(self.backup.id)
     wrong_submit_str = "backups" if self.backup.submit else "submissions"  # intentionally flipped
     correct_submit_str = "submissions" if self.backup.submit else "backups"
     url = "/{0}/{1}/{2}/download/{3}"
     wrong_url = url.format(self.assignment.name, wrong_submit_str,
                            encoded_id, filename)
     redir_url = url.format(self.assignment.name, correct_submit_str,
                            encoded_id, filename)
     response = self.client.get(wrong_url)
     self.assertRedirects(response, redir_url)
     response = self.client.get(redir_url)
     self.assertEqual(contents, response.data.decode('UTF-8'))
Exemple #43
0
def assign_grading(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not assign or not Assignment.can(assign, current_user, 'grade'):
        flash('Cannot access assignment', 'error')
        return abort(404)

    form = forms.CreateTaskForm()
    course_staff = sorted(current_course.get_staff(), key=lambda x: x.role)
    details = lambda e: "{0} - ({1})".format(e.user.email, e.role)
    form.staff.choices = [(utils.encode_id(e.user_id), details(e))
                          for e in course_staff]

    if not form.staff.data:
        # Select all by default
        form.staff.default = [u[0] for u in form.staff.choices]
        form.process()

    if form.validate_on_submit():
        # TODO: Use worker job for this (this is query intensive)
        selected_users = []
        for hash_id in form.staff.data:
            user = User.get_by_id(utils.decode_id(hash_id))
            if user and user.is_enrolled(cid, roles=STAFF_ROLES):
                selected_users.append(user)

        # Available backups
        data = assign.course_submissions()
        backups = set(b['backup']['id'] for b in data if b['backup'])
        students = set(b['user']['id'] for b in data if b['backup'])
        no_submissions = set(b['user']['id'] for b in data if not b['backup'])

        tasks = GradingTask.create_staff_tasks(backups, selected_users, aid,
                                               cid, form.kind.data,
                                               form.only_unassigned.data)

        num_with_submissions = len(students) - len(no_submissions)
        flash(("Created {0} tasks ({1} students) for {2} staff.".format(
            len(tasks), num_with_submissions, len(selected_users))), "success")
        return redirect(url_for('.assignment', cid=cid, aid=aid))

    # Return template with options for who has to grade.
    return render_template('staff/grading/assign_tasks.html',
                           current_course=current_course,
                           assignment=assign,
                           form=form)
Exemple #44
0
        def test_admin_student_assign_diff_overview(self):
            self._login(role="admin")

            backups = []
            for _ in range(3):
                backup = self._gen_backup(self.user1, self.assignment,
                                          "hello{}".format(_), _)
                backups.append(backup)
                models.db.session.add(backup)
            models.db.session.commit()
            bid = utils.encode_id(backups[0].id)

            self.page_load(self.get_server_url() +
                           "/admin/course/1/{}/{}/{}?student_email={}".format(
                               self.user1.email, self.assignment.id, bid,
                               self.user1.email))
            self.assertIn('Diff Overview', self.driver.title)
Exemple #45
0
        def test_assignment_send_backup_to_ag(self):
            self._login(role="admin")
            self.assignment.autograding_key = "test" # Autograder will respond with 200
            models.db.session.commit()

            # find a backup
            backup = models.Backup(
                submitter_id=self.user1.id,
                assignment=self.assignment,
            )
            models.db.session.add(backup)
            models.db.session.commit()

            bid = utils.encode_id(backup.id)

            self.page_load(self.get_server_url() + "/admin/grading/" + bid)
            self.driver.find_element_by_id('autograde-button').click()
            self.assertIn("Submitted to the autograder", self.driver.page_source)
Exemple #46
0
def send_batch(token, assignment, backup_ids, priority='default'):
    """Send a batch of backups to the autograder, returning a dict mapping
    backup ID -> autograder job ID.
    """
    if not assignment.autograding_key:
        raise ValueError('Assignment has no autograder key')

    response_json = send_autograder('/api/ok/v3/grade/batch', {
        'subm_ids': [utils.encode_id(bid) for bid in backup_ids],
        'assignment': assignment.autograding_key,
        'access_token': token.access_token,
        'priority': priority,
        'ok-server-version': 'v3',
    }, autograder_url=assignment.course.autograder_url)
    if response_json:
        return dict(zip(backup_ids, response_json['jobs']))
    else:
        return {}
Exemple #47
0
def send_batch(token, assignment, backup_ids, priority='default'):
    """Send a batch of backups to the autograder, returning a dict mapping
    backup ID -> autograder job ID.
    """
    if not assignment.autograding_key:
        raise ValueError('Assignment has no autograder key')

    response_json = send_autograder(
        '/api/ok/v3/grade/batch', {
            'subm_ids': [utils.encode_id(bid) for bid in backup_ids],
            'assignment': assignment.autograding_key,
            'access_token': token.access_token,
            'priority': priority,
            'ok-server-version': 'v3',
        })
    if response_json:
        return dict(zip(backup_ids, response_json['jobs']))
    else:
        return {}
Exemple #48
0
def export_assignment(assignment_id, anonymized):
    """ Generate a zip file of submissions from enrolled students.

    Final Submission: One submission per student/group
        Zip Strucutre: cal-cs61a../[email protected]@b.com/abc12d/hog.py
    Anonymized: Submission without identifying info
        Zip Strucutre: cal-cs61a../{hash}/hog.py
    """
    logger = jobs.get_job_logger()

    assignment = Assignment.query.get(assignment_id)
    requesting_user = jobs.get_current_job().user

    if not assignment:
        logger.warning("No assignment found")
        raise Exception("No Assignment")

    if not Assignment.can(assignment, requesting_user, "download"):
        raise Exception("{} does not have enough permission"
                        .format(requesting_user.email))
    if anonymized:
        logger.info("Starting anonymized submission export")
    else:
        logger.info("Starting final submission export")
    course = assignment.course
    with io.BytesIO() as bio:
        # Get a handle to the in-memory zip in append mode
        with zipfile.ZipFile(bio, "w", zipfile.ZIP_DEFLATED, False) as zf:
            zf.external_attr = 0o655 << 16
            export_loop(bio, zf, logger, assignment, anonymized)
            created_time = local_time(dt.datetime.now(), course, fmt='%m-%d-%I-%M-%p')
            zip_name = '{}_{}.zip'.format(assignment.name.replace('/', '-'), created_time)

        bio.seek(0)
        # Close zf handle to finish writing zipfile
        logger.info("Uploading...")
        upload = ExternalFile.upload(bio, user_id=requesting_user.id, name=zip_name,
                                     course_id=course.id,
                                     prefix='jobs/exports/{}/'.format(course.offering))

    logger.info("Saved as: {0}".format(upload.object_name))
    msg = "/files/{0}".format(encode_id(upload.id))
    return msg
Exemple #49
0
        def test_get_comments(self):
            self._test_backup(True)
            user = User.lookup(self.user1.email)
            staff = User.lookup(self.staff1.email)
            backup = Backup.query.filter(Backup.submitter_id == user.id).first()
            comment_url = "/api/v3/backups/{}/comment/".format(encode_id(backup.id))
            comment1 = Comment(
                backupid = backup,
                author_id = staff.id,
                filename = 'fizzbuzz.py',
                line = 2,
                message = 'hello world'
            )
            comment2 = Comment(
                backupid = backup,
                author_id = staff.id,
                filename = 'fizzbuzz.py',
                line = 5,
                message = 'wow'
            )
            db.session.add(comment1)
            db.session.add(comment2)

            #check to see if student can view comments on own backup's comments
            self.login(self.user1.email)
            response = self.client.get(comment_url)
            self.assert_200(response)
            self.assertEqual(len(response['data']['comments']), 2)
            self.assertEqual(response['data']['comments'][0].message, 'hello world')
            self.assertEqual(response['data']['comments'][1].message, 'wow')
            self.logout()

            #check to see if staff can access comments
            self.login(self.staff1.email)
            response = self.client.get(comment_url)
            self.assert_200(response)
            self.logout()

            #check to see another student can't see others' backup's comments
            self.login(self.user2.email)
            response = self.client.get(comment_url)
            self.assert_403(response)
            self.logout()
Exemple #50
0
        def test_assignment_send_backup_to_ag(self):
            self._login(role="admin")
            self.assignment.autograding_key = "test"  # Autograder will respond with 200
            models.db.session.commit()

            # find a backup
            backup = models.Backup(
                submitter_id=self.user1.id,
                assignment=self.assignment,
            )
            models.db.session.add(backup)
            models.db.session.commit()

            bid = utils.encode_id(backup.id)

            self.page_load(self.get_server_url() + "/admin/grading/" + bid)
            self.driver.find_element_by_id('autograde-button').click()
            self.assertIn("Submitted to the autograder",
                          self.driver.page_source)
Exemple #51
0
def assign_grading(cid, aid):
    courses, current_course = get_courses(cid)
    assign = Assignment.query.filter_by(id=aid, course_id=cid).one_or_none()
    if not assign or not Assignment.can(assign, current_user, 'grade'):
        flash('Cannot access assignment', 'error')
        return abort(404)

    form = forms.CreateTaskForm()
    course_staff = sorted(current_course.get_staff(), key=lambda x: x.role)
    details = lambda e: "{0} - ({1})".format(e.user.email, e.role)
    form.staff.choices = [(utils.encode_id(e.user_id), details(e))
                          for e in course_staff]

    if not form.staff.data:
        # Select all by default
        form.staff.default = [u[0] for u in form.staff.choices]
        form.process()

    if form.validate_on_submit():
        # TODO: Use worker job for this (this is query intensive)
        selected_users = []
        for hash_id in form.staff.data:
            user = User.get_by_id(utils.decode_id(hash_id))
            if user and user.is_enrolled(cid, roles=STAFF_ROLES):
                selected_users.append(user)

        # Available backups:
        students, backups, no_submissions = assign.course_submissions()

        tasks = GradingTask.create_staff_tasks(backups, selected_users, aid, cid,
                                               form.kind.data, form.only_unassigned.data)

        num_with_submissions = len(students) - len(no_submissions)
        flash(("Created {0} tasks ({1} students) for {2} staff."
               .format(len(tasks), num_with_submissions, len(selected_users))),
              "success")
        return redirect(url_for('.assignment', cid=cid, aid=aid))

    # Return template with options for who has to grade.
    return render_template('staff/grading/assign_tasks.html',
                           current_course=current_course, assignment=assign,
                           form=form)
Exemple #52
0
        def test_get_comments(self):
            self._test_backup(True)
            user = User.lookup(self.user1.email)
            staff = User.lookup(self.staff1.email)
            backup = Backup.query.filter(
                Backup.submitter_id == user.id).first()
            comment_url = "/api/v3/backups/{}/comment/".format(
                encode_id(backup.id))
            comment1 = Comment(backupid=backup,
                               author_id=staff.id,
                               filename='fizzbuzz.py',
                               line=2,
                               message='hello world')
            comment2 = Comment(backupid=backup,
                               author_id=staff.id,
                               filename='fizzbuzz.py',
                               line=5,
                               message='wow')
            db.session.add(comment1)
            db.session.add(comment2)

            #check to see if student can view comments on own backup's comments
            self.login(self.user1.email)
            response = self.client.get(comment_url)
            self.assert_200(response)
            self.assertEqual(len(response['data']['comments']), 2)
            self.assertEqual(response['data']['comments'][0].message,
                             'hello world')
            self.assertEqual(response['data']['comments'][1].message, 'wow')
            self.logout()

            #check to see if staff can access comments
            self.login(self.staff1.email)
            response = self.client.get(comment_url)
            self.assert_200(response)
            self.logout()

            #check to see another student can't see others' backup's comments
            self.login(self.user2.email)
            response = self.client.get(comment_url)
            self.assert_403(response)
            self.logout()
Exemple #53
0
def export_grades():
    logger = jobs.get_job_logger()
    current_user = jobs.get_current_job().user
    course = Course.query.get(jobs.get_current_job().course_id)
    assignments = course.assignments
    students = (Enrollment.query
      .options(db.joinedload('user'))
      .filter(Enrollment.role == STUDENT_ROLE, Enrollment.course == course)
      .all())

    headers, assignments = get_headers(assignments)
    logger.info("Using these headers:")
    for header in headers:
        logger.info('\t' + header)
    logger.info('')

    total_students = len(students)
    with io.StringIO() as f:
        writer = csv.writer(f)
        writer.writerow(headers) # write headers

        for i, student in enumerate(students, start=1):
            row = export_student_grades(student, assignments)
            writer.writerow(row)
            if i % 50 == 0:
                logger.info('Exported {}/{}'.format(i, total_students))
        f.seek(0)
        created_time = local_time(dt.datetime.now(), course, fmt='%b-%-d %Y at %I-%M%p')
        csv_filename = '{course_name} Grades ({date}).csv'.format(
                course_name=course.display_name, date=created_time)
        # convert to bytes for csv upload
        csv_bytes = io.BytesIO(bytearray(f.read(), 'utf-8'))
        upload = ExternalFile.upload(csv_bytes, user_id=current_user.id, name=csv_filename,
                         course_id=course.id,
                         prefix='jobs/exports/{}/'.format(course.offering))

    logger.info('\nDone!\n')
    logger.info("Saved as: {0}".format(upload.object_name))
    return "/files/{0}".format(encode_id(upload.id))
Exemple #54
0
def send_batch(assignment, backup_ids):
    if not assignment.autograding_key:
        raise ValueError('Assignment has no autograder key')

    # Create an access token for this run
    autograder_client = Client.query.get('autograder')
    if not autograder_client:
        autograder_client = Client(
            name='Autograder',
            client_id='autograder',
            client_secret='autograder',
            redirect_uris=[],
            is_confidential=False,
            description='The Autopy autograder system',
            default_scopes=['all'],
        )
        db.session.add(autograder_client)
        db.session.commit()
    token = Token(
        client=autograder_client,
        user=current_user,
        token_type='bearer',
        access_token=oauthlib.common.generate_token(),
        expires=datetime.datetime.utcnow() + datetime.timedelta(hours=2),
        scopes=['all'],
    )
    db.session.add(token)
    db.session.commit()

    return send_autograder('/api/ok/v3/grade/batch', {
        'subm_ids': [utils.encode_id(bid) for bid in backup_ids],
        'assignment': assignment.autograding_key,
        'access_token': token.access_token,
        'priority': 'default',
        'backup_url': url_for('api.backup', _external=True),
        'ok-server-version': 'v3',
    })
Exemple #55
0
 def test_edit_extension(self):
     ext = self._make_ext(self.assignment, self.user1)
     self.login(self.staff1.email)
     expires = (dt.datetime.utcnow() + dt.timedelta(days=3)).strftime(constants.ISO_DATETIME_FMT)
     custom_submission_time = dt.datetime.utcnow().strftime(constants.ISO_DATETIME_FMT)
     message = 'Sickness'
     data = {
         'assignment_id': self.assignment.id,
         'email': self.user1.email,
         'expires': expires,
         'reason': message,
         'submission_time': 'other',
         'custom_submission_time': custom_submission_time
     }
     self.assert200(self.client.post('/admin/course/{}/extensions/{}'.format(self.course.id, utils.encode_id(ext.id)),
                     data=data, follow_redirects=True))
     extension = Extension.get_extension(self.user1, self.assignment, time=dt.datetime.utcnow())
     self.assertEqual(extension.staff_id, self.staff1.id)
     self.assertEqual(extension.assignment_id, self.assignment.id)
     self.assertEqual(extension.message, message)
     self.assertEqual(utils.local_time_obj(extension.expires, self.course).replace(tzinfo=None), dt.datetime.strptime(expires, '%Y-%m-%d %H:%M:%S'))
     self.assertEqual(utils.local_time_obj(extension.custom_submission_time, self.course).replace(tzinfo=None), dt.datetime.strptime(custom_submission_time, '%Y-%m-%d %H:%M:%S'))
Exemple #56
0
 def to_url(self, value):
     return utils.encode_id(value)
Exemple #57
0
def moss_submit(moss_id, submissions, ref_submissions, language, template,
            review_threshold=101, max_matches=MAX_MATCHES, file_regex='.*',
            num_results=NUM_RESULTS):
    """ Sends SUBMISSIONS and REF_SUBMISSIONS to Moss using MOSS_ID,
    LANGUAGE, and MAX_MATCHES.
    Stores results involving SUBMISSIONS in database.
    """

    # ISSUE:  Does not work for .ipynb files well (maybe just use sources?)

    logger = jobs.get_job_logger()
    logger.info('Connecting to Moss...')
    moss = socket.socket()
    moss.connect(('moss.stanford.edu', 7690))
    moss.send('moss {}\n'.format(moss_id).encode())
    moss.send('directory 1\n'.encode())
    moss.send('X 0\n'.encode())
    moss.send('maxmatches {}\n'.format(max_matches).encode())
    moss.send('show {}\n'.format(num_results).encode())
    print(num_results)
    moss.send('language {}\n'.format(language).encode())
    moss_success = moss.recv(1024).decode().strip()
    print(moss_success)
    moss_success = moss_success == 'yes'
    if not moss_success:
        moss.close()
        logger.info('FAILED to connect to Moss.  Common issues:') 
        logger.info('- Make sure your Moss ID is a number, and not your email address.')
        logger.info('- Check you typed your Moss ID correctly.')
        return

    subm_keys = set()
    hashed_subm_keys = set()
    for subm in submissions:
        subm_keys.add(subm['backup']['id'])
        hashed_subm_keys.add(encode_id(subm['backup']['id']))
    for subm in ref_submissions:
        subm_keys.add(subm['backup']['id'])

    backup_query = (Backup.query.options(db.joinedload('messages'))
                          .filter(Backup.id.in_(subm_keys))
                          .order_by(Backup.created.desc())
                          .all())

    match_pattern = re.compile(file_regex)
    if template:
        logger.info('Uploading template...')
        merged_contents = ""
        for filename in template:
            if filename == 'submit' or not match_pattern.match(filename):
                continue
            merged_contents += template[filename] + '\n'
        send_file(moss, 'allcode', merged_contents, 0, language)
    fid = 0
    logger.info('Uploading submissions...')
    for backup in backup_query:
        file_contents = [m for m in backup.messages if m.kind == 'file_contents']
        if not file_contents:
            logger.info("{} didn't have any file contents".format(backup.hashid))
            continue
        contents = file_contents[0].contents
        merged_contents = ""
        for filename in sorted(contents.keys()):
            if filename == 'submit' or not match_pattern.match(filename):
                continue
            merged_contents += contents[filename] + '\n'
        fid += 1
        path = os.path.join(backup.hashid, 'allcode')
        send_file(moss, path, merged_contents, fid, language)
    moss.send("query 0 Submitted via okpy.org\n".encode())
    logger.info('Awaiting response...')
    url = moss.recv(1024).decode().strip()
    moss.send("end\n".encode())
    moss.close()
    logger.info('Moss results at: {}'.format(url))
    parse_moss_results(url, hashed_subm_keys, logger, match_pattern,
                    template, review_threshold)
Exemple #58
0
 def format(self, value):
     if type(value) == int:
         return encode_id(value)
     else:
         return decode_id(value)