def download_submission(submission_hash): """Download a submission from the server. Parameters ---------- submission_hash : str The submission hash of the current submission. """ submission = (Submission.query.filter_by( hash_=submission_hash).one_or_none()) if submission is None: error_str = 'Missing submission: {}'.format(submission_hash) return redirect_to_user(error_str) access_code = is_accessible_code(db.session, submission.event_team.event.name, flask_login.current_user.name, submission.id) if not access_code: error_str = 'Unauthorized access: {}'.format(submission_hash) return redirect_to_user(error_str) file_in_memory = io.BytesIO() with zipfile.ZipFile(file_in_memory, 'w') as zf: for ff in submission.files: data = zipfile.ZipInfo(ff.f_name) data.date_time = time.localtime(time.time())[:6] data.compress_type = zipfile.ZIP_DEFLATED zf.writestr(data, ff.get_code()) file_in_memory.seek(0) return send_file( file_in_memory, attachment_filename=f"submission_{submission.id}.zip", as_attachment=True, )
def toggle_competition(submission_hash): """Pulling out or putting a submission back into competition. Parameters ---------- submission_hash : str The submission hash of the current submission. """ submission = (Submission.query.filter_by( hash_=submission_hash).one_or_none()) if submission is None: error_str = 'Missing submission: {}'.format(submission_hash) return redirect_to_user(error_str) access_code = is_accessible_code(db.session, submission.event_team.event.name, flask_login.current_user.name, submission.id) if not access_code: error_str = 'Missing submission: {}'.format(submission_hash) return redirect_to_user(error_str) submission.is_in_competition = not submission.is_in_competition db.session.commit() update_leaderboards(db.session, submission.event_team.event.name) return redirect('/{}/{}'.format(submission_hash, submission.files[0].f_name))
def test_is_accessible_code(session_toy_db): event_name = 'iris_test' # simulate a user which is not authenticated user = get_user_by_name(session_toy_db, 'test_user_2') user.is_authenticated = False assert not is_accessible_code(session_toy_db, event_name, user.name) # simulate a user which authenticated and author of the submission to a # public event user.is_authenticated = True assert is_accessible_code(session_toy_db, event_name, user.name) # simulate an admin user user = get_user_by_name(session_toy_db, 'test_iris_admin') user.is_authenticated = True assert is_accessible_code(session_toy_db, event_name, 'test_iris_admin') # simulate a user which is not signed up to the event user = add_user(session_toy_db, 'xx', 'xx', 'xx', 'xx', 'xx', 'user') user.is_authenticated = True assert not is_accessible_code(session_toy_db, event_name, user.name)
def my_submissions(event_name): """Landing page of all user's submission information. Parameters ---------- event_name : str The name of the event. """ event = get_event(db.session, event_name) if not is_accessible_event(db.session, event_name, flask_login.current_user.name): return redirect_to_user( '{}: no event named "{}"' .format(flask_login.current_user.firstname, event_name) ) if app.config['TRACK_USER_INTERACTION']: add_user_interaction( db.session, interaction='looking at my_submissions', user=flask_login.current_user, event=event ) if not is_accessible_code(db.session, event_name, flask_login.current_user.name): error_str = ('No access to my submissions for event {}. If you have ' 'already signed up, please wait for approval.' .format(event.name)) return redirect_to_user(error_str) # Doesn't work if team mergers are allowed event_team = get_event_team_by_name(db.session, event_name, flask_login.current_user.name) leaderboard_html = event_team.leaderboard_html failed_leaderboard_html = event_team.failed_leaderboard_html new_leaderboard_html = event_team.new_leaderboard_html admin = is_admin(db.session, event_name, flask_login.current_user.name) if event.official_score_type.is_lower_the_better: sorting_direction = 'asc' else: sorting_direction = 'desc' return render_template('leaderboard.html', leaderboard_title='Trained submissions', leaderboard=leaderboard_html, failed_leaderboard=failed_leaderboard_html, new_leaderboard=new_leaderboard_html, sorting_column_index=4, sorting_direction=sorting_direction, event=event, admin=admin)
def test_is_accessible_code(session_toy_db): # create a third user add_user( session_toy_db, name='test_user_3', password='******', lastname='Test_3', firstname='User_3', email='*****@*****.**', access_level='user') approve_user(session_toy_db, 'test_user_3') event_name = 'iris_test' sign_up_team(session_toy_db, event_name, 'test_user_3') # simulate a user which is not authenticated user = get_user_by_name(session_toy_db, 'test_user_2') user.is_authenticated = False assert not is_accessible_code(session_toy_db, event_name, user.name) # simulate a user which authenticated and author of the submission to a # public event user.is_authenticated = True assert is_accessible_code(session_toy_db, event_name, user.name) # simulate an admin user user = get_user_by_name(session_toy_db, 'test_iris_admin') user.is_authenticated = True assert is_accessible_code(session_toy_db, event_name, 'test_iris_admin') # simulate a user which is not signed up to the event user = add_user(session_toy_db, 'xx', 'xx', 'xx', 'xx', 'xx', 'user') user.is_authenticated = True assert not is_accessible_code(session_toy_db, event_name, user.name) # simulate that the event is not publicly opened event = get_event(session_toy_db, event_name) past_public_opening = event.public_opening_timestamp tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1) event.public_opening_timestamp = tomorrow session_toy_db.commit() assert is_accessible_code(session_toy_db, event_name, 'test_user_3') # Make a submission submission_name = 'random_forest_10_10' ramp_config = generate_ramp_config(read_config(ramp_config_template())) path_submission = os.path.join( os.path.dirname(ramp_config['ramp_sandbox_dir']), submission_name ) sub = add_submission( session_toy_db, event_name, 'test_user_3', submission_name, path_submission ) # check that the user submitting the submission could access it assert is_accessible_code( session_toy_db, event_name, 'test_user_3', sub.id ) # change the admin of the team from ramp_database.model import Team, User team = (session_toy_db.query(Team) .filter(Team.name == 'test_user_3') .first()) user = (session_toy_db.query(User) .filter(User.name == 'test_user_2') .first()) team.admin_id = user.id team.admin = user session_toy_db.commit() # check that the admin can access the submission assert is_accessible_code( session_toy_db, event_name, 'test_user_2', sub.id ) # but others cannot assert not is_accessible_code( session_toy_db, event_name, 'test_user_3', sub.id ) event.public_opening_timestamp = past_public_opening session_toy_db.commit()
def view_model(submission_hash, f_name): """Rendering submission codes using templates/submission.html. The code of f_name is displayed in the left panel, the list of submissions files is in the right panel. Clicking on a file will show that file (using the same template). Clicking on the name on the top will download the file itself (managed in the template). Clicking on "Archive" will zip all the submission files and download them (managed here). Parameters ---------- submission_hash : str The hash_ of the submission. f_name : tr The name of the submission file. """ submission = (Submission.query.filter_by( hash_=submission_hash).one_or_none()) if (submission is None or not is_accessible_code( db.session, submission.event.name, flask_login.current_user.name, submission.name)): error_str = u'Missing submission: {}'.format(submission_hash) return redirect_to_user(error_str) event = submission.event_team.event team = submission.event_team.team workflow_element_name = f_name.split('.')[0] workflow_element = \ (WorkflowElement.query.filter_by(name=workflow_element_name, workflow=event.workflow) .one_or_none()) if workflow_element is None: error_str = (u'{} is not a valid workflow element by {} '.format( workflow_element_name, flask_login.current_user.name)) error_str += u'in {}/{}/{}/{}'.format(event, team, submission, f_name) return redirect_to_user(error_str) submission_file = \ (SubmissionFile.query.filter_by(submission=submission, workflow_element=workflow_element) .one_or_none()) if submission_file is None: error_str = (u'No submission file by {} in {}/{}/{}/{}'.format( flask_login.current_user.name, event, team, submission, f_name)) return redirect_to_user(error_str) # superfluous, perhaps when we'll have different extensions? f_name = submission_file.f_name submission_abspath = os.path.abspath(submission.path) if not os.path.exists(submission_abspath): error_str = (u'{} does not exist by {} in {}/{}/{}/{}'.format( submission_abspath, flask_login.current_user.name, event, team, submission, f_name)) return redirect_to_user(error_str) if app.config['TRACK_USER_INTERACTION']: add_user_interaction(db.session, interaction='looking at submission', user=flask_login.current_user, event=event, submission=submission, submission_file=submission_file) logger.info(u'{} is looking at {}/{}/{}/{}'.format( flask_login.current_user.name, event, team, submission, f_name)) # Downloading file if it is not editable (e.g., external_data.csv) if not workflow_element.is_editable: # archive_filename = f_name + '.zip' # with changedir(submission_abspath): # with ZipFile(archive_filename, 'w') as archive: # archive.write(f_name) if app.config['TRACK_USER_INTERACTION']: add_user_interaction(db.session, interaction='download', user=flask_login.current_user, event=event, submission=submission, submission_file=submission_file) return send_from_directory(submission_abspath, f_name, as_attachment=True, attachment_filename=u'{}_{}'.format( submission.hash_[:6], f_name), mimetype='application/octet-stream') # Importing selected files into sandbox choices = [(f, f) for f in submission.f_names] import_form = ImportForm() import_form.selected_f_names.choices = choices if import_form.validate_on_submit(): sandbox_submission = get_submission_by_name( db.session, event.name, flask_login.current_user.name, event.ramp_sandbox_name) for filename in import_form.selected_f_names.data: logger.info(u'{} is importing {}/{}/{}/{}'.format( flask_login.current_user.name, event, team, submission, filename)) # TODO: deal with different extensions of the same file src = os.path.join(submission.path, filename) dst = os.path.join(sandbox_submission.path, filename) shutil.copy2(src, dst) # copying also metadata logger.info(u'Copying {} to {}'.format(src, dst)) workflow_element = WorkflowElement.query.filter_by( name=filename.split('.')[0], workflow=event.workflow).one() submission_file = SubmissionFile.query.filter_by( submission=submission, workflow_element=workflow_element).one() if app.config['TRACK_USER_INTERACTION']: add_user_interaction(db.session, interaction='copy', user=flask_login.current_user, event=event, submission=submission, submission_file=submission_file) return redirect(u'/events/{}/sandbox'.format(event.name)) with open(os.path.join(submission.path, f_name)) as f: code = f.read() admin = is_admin(db.session, event.name, flask_login.current_user.name) return render_template('submission.html', event=event, code=code, submission=submission, f_name=f_name, import_form=import_form, admin=admin)
def credit(submission_hash): """The landing page to credit other submission when a user submit is own. Parameters ---------- submission_hash : str The submission hash of the current submission. """ submission = (Submission.query.filter_by( hash_=submission_hash).one_or_none()) access_code = is_accessible_code(db.session, submission.event_team.event.name, flask_login.current_user.name, submission.name) if submission is None or not access_code: error_str = u'Missing submission: {}'.format(submission_hash) return redirect_to_user(error_str) event_team = submission.event_team event = event_team.event source_submissions = get_source_submissions(db.session, submission.id) def get_s_field(source_submission): return u'{}/{}/{}'.format(source_submission.event_team.event.name, source_submission.event_team.team.name, source_submission.name) # Make sure that CreditForm is empty CreditForm.name_credits = [] credit_form_kwargs = {} for source_submission in source_submissions: s_field = get_s_field(source_submission) setattr(CreditForm, s_field, StringField(u'Text')) credit_form = CreditForm(**credit_form_kwargs) sum_credit = 0 # new = True for source_submission in source_submissions: s_field = get_s_field(source_submission) submission_similaritys = \ (SubmissionSimilarity.query .filter_by( type='target_credit', user=flask_login.current_user, source_submission=source_submission, target_submission=submission) .all()) if not submission_similaritys: submission_credit = 0 else: # new = False # find the last credit (in case crediter changes her mind) submission_similaritys.sort(key=lambda x: x.timestamp, reverse=True) submission_credit = int( round(100 * submission_similaritys[0].similarity)) sum_credit += submission_credit credit_form.name_credits.append( (s_field, str(submission_credit), source_submission.link)) # This doesnt work, not sure why # if not new: # credit_form.self_credit.data = str(100 - sum_credit) if credit_form.validate_on_submit(): try: sum_credit = int(credit_form.self_credit.data) logger.info(sum_credit) for source_submission in source_submissions: s_field = get_s_field(source_submission) sum_credit += int(getattr(credit_form, s_field).data) if sum_credit != 100: return redirect_to_credit( submission_hash, 'Error: The total credit should add up to 100') except Exception as e: return redirect_to_credit(submission_hash, u'Error: {}'.format(e)) for source_submission in source_submissions: s_field = get_s_field(source_submission) similarity = int(getattr(credit_form, s_field).data) / 100. submission_similarity = \ (SubmissionSimilarity.query .filter_by( type='target_credit', user=flask_login.current_user, source_submission=source_submission, target_submission=submission) .all()) # if submission_similarity is not empty, we need to # add zero to cancel previous credits explicitly if similarity > 0 or submission_similarity: add_submission_similarity(db.session, credit_type='target_credit', user=flask_login.current_user, source_submission=source_submission, target_submission=submission, similarity=similarity, timestamp=datetime.datetime.utcnow()) if app.config['TRACK_USER_INTERACTION']: add_user_interaction(db.session, interaction='giving credit', user=flask_login.current_user, event=event, submission=submission) return redirect(u'/events/{}/sandbox'.format(event.name)) admin = is_admin(db.session, event.name, flask_login.current_user.name) return render_template('credit.html', submission=submission, source_submissions=source_submissions, credit_form=credit_form, event=event, admin=admin)
def sandbox(event_name): """Landing page for the user's sandbox. Parameters ---------- event_name : str The event name. """ event = get_event(db.session, event_name) if not is_accessible_event(db.session, event_name, flask_login.current_user.name): return redirect_to_user(u'{}: no event named "{}"'.format( flask_login.current_user.firstname, event_name)) if not is_accessible_code(db.session, event_name, flask_login.current_user.name): error_str = ('No access to sandbox for event {}. If you have ' 'already signed up, please wait for approval.'.format( event.name)) return redirect_to_user(error_str) # setup the webpage when loading # we use the code store in the sandbox to show to the user sandbox_submission = get_submission_by_name(db.session, event_name, flask_login.current_user.name, event.ramp_sandbox_name) event_team = get_event_team_by_name(db.session, event_name, flask_login.current_user.name) # initialize the form for the code # The amount of python magic we have to do for rendering a variable # number of textareas, named and populated at run time, is mind # boggling. # First we need to make sure CodeForm is empty # for name_code in CodeForm.names_codes: # name, _ = name_code # delattr(CodeForm, name) CodeForm.names_codes = [] # Then we create named fields in the CodeForm class for each editable # submission file. They have to be populated when the code_form object # is created, so we also create a code_form_kwargs dictionary and # populate it with the codes. code_form_kwargs = {} for submission_file in sandbox_submission.files: if submission_file.is_editable: f_field = submission_file.name setattr(CodeForm, f_field, StringField(u'Text', widget=TextArea())) code_form_kwargs[f_field] = submission_file.get_code() code_form_kwargs['prefix'] = 'code' code_form = CodeForm(**code_form_kwargs) # Then, to be able to iterate over the files in the sandbox.html # template, we also fill a separate table of pairs (file name, code). # The text areas in the template will then have to be created manually. for submission_file in sandbox_submission.files: if submission_file.is_editable: code_form.names_codes.append( (submission_file.name, submission_file.get_code())) # initialize the submission field and the the uploading form submit_form = SubmitForm(submission_name=event_team.last_submission_name, prefix='submit') upload_form = UploadForm(prefix='upload') admin = is_admin(db.session, event_name, flask_login.current_user.name) if request.method == 'GET': return render_template('sandbox.html', submission_names=sandbox_submission.f_names, code_form=code_form, submit_form=submit_form, upload_form=upload_form, event=event, admin=admin) if request.method == 'POST': if ('code-csrf_token' in request.form and code_form.validate_on_submit()): try: for submission_file in sandbox_submission.files: if submission_file.is_editable: old_code = submission_file.get_code() submission_file.set_code( request.form[submission_file.name]) new_code = submission_file.get_code() diff = '\n'.join( difflib.unified_diff(old_code.splitlines(), new_code.splitlines())) similarity = difflib.SequenceMatcher( a=old_code, b=new_code).ratio() if app.config['TRACK_USER_INTERACTION']: add_user_interaction( db.session, interaction='save', user=flask_login.current_user, event=event, submission_file=submission_file, diff=diff, similarity=similarity) except Exception as e: return redirect_to_sandbox(event, u'Error: {}'.format(e)) return redirect_to_sandbox( event, 'You submission has been saved. You can safely comeback to ' 'your sandbox later.', is_error=False, category='File saved') elif request.files: upload_f_name = secure_filename(request.files['file'].filename) upload_name = upload_f_name.split('.')[0] # TODO: create a get_function upload_workflow_element = WorkflowElement.query.filter_by( name=upload_name, workflow=event.workflow).one_or_none() if upload_workflow_element is None: return redirect_to_sandbox( event, u'{} is not in the file list.'.format(upload_f_name)) # TODO: create a get_function submission_file = SubmissionFile.query.filter_by( submission=sandbox_submission, workflow_element=upload_workflow_element).one() if submission_file.is_editable: old_code = submission_file.get_code() tmp_f_name = os.path.join(tempfile.gettempdir(), upload_f_name) request.files['file'].save(tmp_f_name) file_length = os.stat(tmp_f_name).st_size if (upload_workflow_element.max_size is not None and file_length > upload_workflow_element.max_size): return redirect_to_sandbox( event, u'File is too big: {} exceeds max size {}'.format( file_length, upload_workflow_element.max_size)) if submission_file.is_editable: try: with open(tmp_f_name) as f: code = f.read() submission_file.set_code(code) except Exception as e: return redirect_to_sandbox(event, u'Error: {}'.format(e)) else: # non-editable files are not verified for now dst = os.path.join(sandbox_submission.path, upload_f_name) shutil.copy2(tmp_f_name, dst) logger.info(u'{} uploaded {} in {}'.format( flask_login.current_user.name, upload_f_name, event)) if submission_file.is_editable: new_code = submission_file.get_code() diff = '\n'.join( difflib.unified_diff(old_code.splitlines(), new_code.splitlines())) similarity = difflib.SequenceMatcher(a=old_code, b=new_code).ratio() if app.config['TRACK_USER_INTERACTION']: add_user_interaction(db.session, interaction='upload', user=flask_login.current_user, event=event, submission_file=submission_file, diff=diff, similarity=similarity) else: if app.config['TRACK_USER_INTERACTION']: add_user_interaction(db.session, interaction='upload', user=flask_login.current_user, event=event, submission_file=submission_file) return redirect(request.referrer) # TODO: handle different extensions for the same workflow element # ie: now we let upload eg external_data.bla, and only fail at # submission, without giving a message elif ('submit-csrf_token' in request.form and submit_form.validate_on_submit()): new_submission_name = request.form['submit-submission_name'] if not 4 < len(new_submission_name) < 20: return redirect_to_sandbox( event, 'Submission name should have length between 4 and ' '20 characters.') try: new_submission_name.encode('ascii') except Exception as e: return redirect_to_sandbox(event, u'Error: {}'.format(e)) try: new_submission = add_submission(db.session, event_name, event_team.team.name, new_submission_name, sandbox_submission.path) except DuplicateSubmissionError: return redirect_to_sandbox( event, u'Submission {} already exists. Please change the name.'. format(new_submission_name)) except MissingExtensionError as e: return redirect_to_sandbox(event, 'Missing extension') except TooEarlySubmissionError as e: return redirect_to_sandbox(event, str(e)) logger.info(u'{} submitted {} for {}.'.format( flask_login.current_user.name, new_submission.name, event_team)) if event.is_send_submitted_mails: admin_users = User.query.filter_by(access_level='admin') for admin in admin_users: subject = 'Submission {} sent for training'.format( new_submission.name) body = """A new submission have been submitted: event: {} user: {} submission: {} submission path: {} """.format(event_team.event.name, flask_login.current_user.name, new_submission.name, new_submission.path) send_mail(admin, subject, body) if app.config['TRACK_USER_INTERACTION']: add_user_interaction(db.session, interaction='submit', user=flask_login.current_user, event=event, submission=new_submission) return redirect_to_sandbox(event, u'{} submitted {} for {}'.format( flask_login.current_user.firstname, new_submission.name, event_team), is_error=False, category='Submission') admin = is_admin(db.session, event_name, flask_login.current_user.name) return render_template('sandbox.html', submission_names=sandbox_submission.f_names, code_form=code_form, submit_form=submit_form, upload_form=upload_form, event=event, admin=admin)