Пример #1
0
 def submit_evaluations(self):
     me = request.identity['user']
     students_allowed = set(user.id for user in self.group.users)
     for user_id, score in request.POST.items():
         user_id = int(user_id)
         if user_id not in students_allowed:
             abort(403, "You cannot submit an evaluation to this user")
         score = float(score)
         if score < 0:
             abort(403, "Negative contributions are not allowed")
         e = (DBSession.query(Evaluation)
                       .filter(Evaluation.from_student_id == me.id)
                       .filter(Evaluation.to_student_id == user_id)
                       .filter(Evaluation.group_id == self.group.id)
                       .one_or_none())
         if not e:
             e = Evaluation(
                 from_student_id=me.id,
                 to_student_id=user_id,
                 group=self.group,
                 score=score)
         else:
             e.score = score
         DBSession.add(e)
     flash('Your evaluations have been saved. Thank you.', 'success')
     redirect('/group/{}'.format(self.group.id))
Пример #2
0
    def authenticate(self, environ, identity):
        try:
            ticket = identity['login']
        except KeyError:
            return None

        if identity['identifier'] != 'mpapi':
            return None

        r = requests.post(self.mpapi_fetch, data={'tkt': ticket})
        r.raise_for_status()
        data = r.json()
        if data['result'] != 'success':
            raise ValueError('MPAPI Failure')
        username = data['uid']

        identity['user'] = User.from_username(username)
        if identity['user']:
            return username
        else:
            user = user_from_mpapi_attributes(data['attributes'])
            DBSession.add(user)
            DBSession.flush()
            transaction.commit()
            return username
Пример #3
0
    def ov(self, output_id):
        output = DBSession.query(Output).get(int(output_id))
        if output.group.competition_id != self.competition.id:
            abort(404)
        if not output.active:
            abort(404, "This output is no longer active.")
        user = request.identity and request.identity['user']
        if user:
            group = (DBSession.query(Group).filter(
                Group.competition_id == self.competition.id).filter(
                    Group.users.any(id=user.id)).first())
        else:
            group = None
        message = request.POST.get('message')
        if group and message:
            if output.use_ground_truth:
                abort(403, "The instructor has already reviewed this output.")

            output.use_ground_truth = True
            protest = Protest(
                message=message,
                accepted=output.verification != output.ground_truth,
                submitter=group,
                output=output)

            DBSession.add(output)
            DBSession.add(protest)

            flash('Your protest has been submitted.', 'success')

        return {
            'output': output,
            'group': group,
            'competition': self.competition
        }
Пример #4
0
 def automatic_verification(self):
     outputs = (DBSession
                .query(Output)
                .join(Output.input)
                .filter(Input.group_id == self.group.id))
     for output in outputs:
         output.verification = output.ground_truth
         DBSession.add(output)
     DBSession.flush()
Пример #5
0
    def input_upload(self, input_upload=None, team_name=None):
        user = request.identity['user']
        if not user.admin and not self.group.competition.input_upload_open:
            abort(403, "Sorry, input upload stage is closed.")

        if hasattr(input_upload, "file"):
            try:
                contents = input_upload.file.read().decode("utf-8")
            except UnicodeDecodeError:
                flash('Your input contains invalid characters. '
                      'Please correct and try uploading again.',
                      'danger')
                redirect(self.base_url)

            problem = problemlib.load_problem(self.group.competition)
            try:
                input = problem.parse_input(StringIO(contents))
            except problemlib.FileFormatError as e:
                flash(
                    f"Your input is not valid: {e}. Please correct and upload again.",
                    "danger",
                )
                redirect(self.base_url)

            reformatted_contents = StringIO()
            input.write(reformatted_contents)

            f = FileIntent(
                BytesIO(reformatted_contents.getvalue().encode('utf-8')),
                'input_group{}.txt'.format(self.group.id),
                'application/octet-stream')
            if self.group.input is None:
                iput = Input(data=f, group=self.group)
                DBSession.add(iput)
            else:
                self.group.input.data = f
            DBSession.flush()

        if team_name is not None:
            if len(team_name) >= 100:
                flash('Your team name has been rejected, as it is too long.',
                      'danger')
                redirect(self.base_url)

            if not team_name:
                flash('You must set a team name.',
                      'danger')
                redirect(self.base_url)

            self.group.name = team_name

        flash('Thank you. Please return here for output upload on {}'
              .format(ftime(self.group.competition.output_upload_begins)),
              'success')
        redirect(self.base_url)
Пример #6
0
    def input_upload(self, input_upload=None, team_name=None):
        user = request.identity['user']
        if not user.admin and not self.group.competition.input_upload_open:
            abort(403, "Sorry, input upload stage is closed.")

        if hasattr(input_upload, "file"):
            try:
                contents = file_normalize(input_upload.file.read())
            except UnicodeDecodeError:
                flash(
                    'Your input contains invalid characters. '
                    'Please correct and try uploading again.', 'danger')
                redirect(self.base_url)

            if len(contents) > 1E6:
                abort(400, "Your input exceeds the maxiumum size.")
            verif_mod = self.group.competition.input_verifier.module

            try:
                verif_mod.verify(StringIO(contents))
            except verif_mod.VerificationError as e:
                flash(
                    'Your input has been rejected for the following reason: '
                    '{}. Please correct and try uploading again.'.format(e),
                    'danger')
                redirect(self.base_url)

            f = FileIntent(BytesIO(contents.encode('utf-8')),
                           'input_group{}.txt'.format(self.group.id),
                           'application/octet-stream')
            if self.group.input is None:
                iput = Input(data=f, group=self.group)
                DBSession.add(iput)
            else:
                self.group.input.data = f
            DBSession.flush()

        if team_name is not None:
            if len(team_name) >= 100:
                flash('Your team name has been rejected, as it is too long.',
                      'danger')
                redirect(self.base_url)

            if not team_name:
                flash('You must set a team name.', 'danger')
                redirect(self.base_url)

            self.group.name = team_name

        flash(
            'Thank you. Please return here for output upload on {}'.format(
                ftime(self.group.competition.output_upload_begins)), 'success')
        redirect(self.base_url)
Пример #7
0
 def new_user_by_username(self, username):
     url = f"{self.mpapi_url}/uid/{username}"
     r = requests.get(url)
     r.raise_for_status()
     data = r.json()
     if data["result"] != "success":
         raise ValueError("MPAPI Failure.  User {username} may not exist?")
     attributes = data["attributes"]
     uid = attributes["uidNumber"]
     full_name = f"{attributes['first']} {attributes['sn']}"
     email = attributes["mail"]
     user = User(id=uid,
                 username=username,
                 full_name=full_name,
                 email=email)
     DBSession.add(user)
     return user
Пример #8
0
    def ov(self, output_id):
        output = DBSession.query(Output).get(int(output_id))
        if output.group.competition_id != self.competition.id:
            abort(404)
        if not output.active:
            abort(404, "This output is no longer active.")
        user = request.identity and request.identity['user']
        if user:
            group = (DBSession.query(Group).filter(
                Group.competition_id == self.competition.id).filter(
                    Group.users.any(id=user.id)).first())
        else:
            group = None

        problem = problemlib.load_problem(self.competition)
        problem_module = problem.get_module()
        show_input_downloads = ((user and user.admin)
                                or not output.group.competition.archived)
        message = request.POST.get('message')
        if group and message:
            if output.use_ground_truth:
                abort(403, "The instructor has already reviewed this output.")

            output.use_ground_truth = True
            protest = Protest(
                message=message,
                accepted=output.verification != output.ground_truth,
                submitter=group,
                output=output)

            DBSession.add(output)
            DBSession.add(protest)

            flash('Your protest has been submitted.', 'success')

        return {
            "output": output,
            "score": problem_module.Output.repr_score(output.score),
            "group": group,
            "show_input_downloads": show_input_downloads,
            "competition": self.competition,
        }
Пример #9
0
    def authenticate(self, environ, identity):
        try:
            ticket = identity['login']
        except KeyError:
            return None

        if identity['identifier'] != 'mpapi':
            return None

        r = requests.post(self.mpapi_fetch, data={'tkt': ticket})
        r.raise_for_status()
        data = r.json()
        if data['result'] != 'success':
            raise ValueError('MPAPI Failure')
        username = data['uid']
        attributes = data['attributes']
        uid = attributes['uidNumber']
        full_name = '{} {}'.format(attributes['first'], attributes['sn'])
        email = attributes['mail']

        user = DBSession.query(User).filter(User.id == uid).one_or_none()
        if user:
            # Update attributes in case of username/full name change
            user.username = username
            user.full_name = full_name
            user.email = email
        else:
            user = User(id=uid,
                        username=username,
                        full_name=full_name,
                        email=email)
            DBSession.add(user)

        DBSession.flush()
        transaction.commit()
        return username
Пример #10
0
    def submit_output(self, to_group, output_file=None):
        user = request.identity['user']
        to_group = DBSession.query(Group).get(to_group)
        if not to_group:
            abort(404, "No such group")
        if to_group.competition_id != self.group.competition_id:
            abort(400, "Cannot submit to a group in another competition")
        if not hasattr(output_file, "file"):
            abort(400, "Must include file in submission")
        comp = self.group.competition
        existing = (DBSession.query(Output)
                             .filter(Output.group_id == self.group.id)
                             .filter(Output.input_id == to_group.input.id)
                             .filter(Output.active == True)
                             .one_or_none())
        if not (user.admin
                or comp.output_upload_open
                or (comp.resolution_open
                    and existing is not None
                    and not existing.use_ground_truth)):
            abort(403, "Forbidden to upload this output at this time")

        try:
            contents = output_file.file.read().decode("utf-8")
        except UnicodeDecodeError:
            return {'status': 'error',
                    'msg': 'Output contains invalid characters.'}

        problem = problemlib.load_problem(comp)

        try:
            output = problem.parse_output(to_group.input.data.file,
                                          StringIO(contents))
        except problemlib.FileFormatError as e:
            return {
                "status": "error",
                "msg": f"Output has formatting errors: {e}",
            }

        new_contents = StringIO()
        output.write(new_contents)

        f = FileIntent(
            BytesIO(new_contents.getvalue().encode("utf-8")),
            'output_from_{}_to_{}.txt'.format(self.group.id, to_group.id),
            'application/octet-stream')
        try:
            output.verify()
        except problemlib.VerificationError:
            ground_truth = VerificationStatus.rejected
        except Exception:
            ground_truth = VerificationStatus.waiting
        else:
            ground_truth = VerificationStatus.accepted

        use_ground_truth = (
            not comp.verification_begins
            or datetime.datetime.now() >= comp.verification_begins
        )

        if existing:
            if comp.resolution_open:
                existing.active = False
            else:
                DBSession.delete(existing)

        db_output = Output(
            data=f,
            group=self.group,
            input=to_group.input,
            score=output.score,
            original=comp.output_upload_open,
            ground_truth=ground_truth,
            use_ground_truth=use_ground_truth,
        )
        DBSession.add(db_output)
        DBSession.flush()

        return {'status': 'success', 'url': db_output.data.url}
Пример #11
0
    def submit_output(self, to_group, output_file=None):
        user = request.identity['user']
        to_group = DBSession.query(Group).get(to_group)
        if not to_group:
            abort(404, "No such group")
        if to_group.competition_id != self.group.competition_id:
            abort(400, "Cannot submit to a group in another competition")
        if not hasattr(output_file, "file"):
            abort(400, "Must include file in submission")
        comp = self.group.competition
        existing = (DBSession.query(Output).filter(
            Output.group_id == self.group.id).filter(
                Output.input_id == to_group.input.id).filter(
                    Output.active == True).one_or_none())
        if not (user.admin or comp.output_upload_open or
                (comp.resolution_open and existing is not None
                 and not existing.use_ground_truth)):
            abort(403, "Forbidden to upload this output at this time")

        try:
            contents = file_normalize(output_file.file.read())
        except UnicodeDecodeError:
            return {
                'status': 'error',
                'msg': 'Output contains invalid characters.'
            }

        if len(contents) > 1E6:
            return {'status': 'error', 'msg': 'Output exceeds maximum size.'}

        try:
            score, _, _ = contents.partition('\n')
            score = int(score)
        except ValueError:
            return {
                'status': 'error',
                'msg': 'First line must only contain an integer.'
            }

        f = FileIntent(
            BytesIO(contents.encode('utf-8')),
            'output_from_{}_to_{}.txt'.format(self.group.id, to_group.id),
            'application/octet-stream')
        if existing:
            if comp.resolution_open:
                existing.active = False
            else:
                DBSession.delete(existing)
        output = Output(data=f,
                        group=self.group,
                        input=to_group.input,
                        score=score,
                        original=comp.output_upload_open)

        verif_mod = self.group.competition.output_verifier.module
        try:
            verif_mod.verify(to_group.input.data.file, StringIO(contents))
        except verif_mod.VerificationError:
            output.ground_truth = VerificationStatus.rejected
        except Exception:
            output.ground_truth = VerificationStatus.waiting
        else:
            output.ground_truth = VerificationStatus.accepted

        if comp.resolution_open or not comp.verification_begins:
            output.use_ground_truth = True

        DBSession.add(output)
        DBSession.flush()

        return {'status': 'success', 'url': output.data.url}