def note(user_id: int): act = request.args.get('act') or request.json.get('act') user = User.get_or_none(User.id == user_id) if user is None: return fail(404, f'No such user {user_id}.') if act == 'fetch': return jsonify(tuple(user.notes().dicts())) if not current_user.role.is_manager: return fail(403, "You aren't allowed to access this page.") if act == 'delete': note_id = int(request.args.get('noteId')) return try_or_fail(notes.delete, note_id=note_id) if act == 'create': note_text = request.args.get('note', '') note_exercise = request.args.get('exercise', '') privacy = request.args.get('privacy', '0') return try_or_fail( notes.create, user=user, note_text=note_text, note_exercise=note_exercise, privacy=privacy, ) return fail(400, f'Unknown or unset act value "{act}".')
def upload_page(): user_id = current_user.id user = User.get_or_none(User.id == user_id) # should never happen if user is None: return fail(404, 'User not found.') if request.content_length > MAX_UPLOAD_SIZE: return fail( 413, f'File is too big. {MAX_UPLOAD_SIZE // 1000000}MB allowed.', ) file: Optional[FileStorage] = request.files.get('file') if file is None: return fail(422, 'No file was given.') try: matches, misses = upload.new(user, file) except UploadError as e: log.debug(e) return fail(400, str(e)) except FileSizeError as e: log.debug(e) return fail(413, str(e)) return jsonify({ 'exercise_matches': matches, 'exercise_misses': misses, })
def view( solution_id: int, file_id: Optional[int] = None, shared_url: str = '', ): solution = Solution.get_or_none(Solution.id == solution_id) if solution is None: return fail(404, 'Solution does not exist.') viewer_is_solver = solution.solver.id == current_user.id has_viewer_access = current_user.role.is_viewer if not shared_url and not viewer_is_solver and not has_viewer_access: return fail(403, 'This user has no permissions to view this page.') is_manager = current_user.role.is_manager solution_files = tuple(solution.files) if not solution_files: if not is_manager: return fail(404, 'There are no files in this solution.') return done_checking(solution.exercise.id, solution.id) try: view_params = solutions.get_view_parameters( solution, file_id, shared_url, is_manager, solution_files, viewer_is_solver, ) except LmsError as e: error_message, status_code = e.args return fail(status_code, error_message) return render_template('view.html', **view_params)
def download(download_id: str): """Downloading a zip file of the code files. Args: download_id (str): Can be on each side of a solution.id and sharedsolution.shared_url. """ solution = Solution.get_or_none(Solution.id == download_id) shared_solution = SharedSolution.get_or_none( SharedSolution.shared_url == download_id, ) if solution is None and shared_solution is None: return fail(404, 'Solution does not exist.') if shared_solution is None: viewer_is_solver = solution.solver.id == current_user.id has_viewer_access = current_user.role.is_viewer if not viewer_is_solver and not has_viewer_access: return fail(403, 'This user has no permissions to view this page.') files = solution.files filename = solution.exercise.subject else: files = shared_solution.solution.files filename = shared_solution.solution.exercise.subject response = make_response(solutions.create_zip_from_solution(files)) response.headers.set('Content-Type', 'zip') response.headers.set( 'Content-Disposition', 'attachment', filename=f'{filename}.zip', ) return response
def view( solution_id: int, file_id: Optional[int] = None, shared_url: str = '', ): solution = Solution.get_or_none(Solution.id == solution_id) if solution is None: return fail(404, 'Solution does not exist.') viewer_is_solver = solution.solver.id == current_user.id has_viewer_access = current_user.role.is_viewer if not shared_url and not viewer_is_solver and not has_viewer_access: return fail(403, 'This user has no permissions to view this page.') versions = solution.ordered_versions() test_results = solution.test_results() is_manager = current_user.role.is_manager solution_files = tuple(solution.files) if not solution_files: if not is_manager: return fail(404, 'There are no files in this solution.') return done_checking(solution.exercise.id, solution.id) files = solutions.get_files_tree(solution.files) file_id = file_id or files[0]['id'] file_to_show = next((f for f in solution_files if f.id == file_id), None) if file_to_show is None: return fail(404, 'File does not exist.') view_params = { 'solution': model_to_dict(solution), 'files': files, 'comments': solution.comments_per_file, 'current_file': file_to_show, 'is_manager': is_manager, 'role': current_user.role.name.lower(), 'versions': versions, 'test_results': test_results, 'shared_url': shared_url, } if is_manager: view_params = { **view_params, 'exercise_common_comments': _common_comments(exercise_id=solution.exercise), 'all_common_comments': _common_comments(), 'user_comments': _common_comments(user_id=current_user.id), 'left': Solution.left_in_exercise(solution.exercise), } if viewer_is_solver: notifications.read_related(solution_id, current_user.id) return render_template('view.html', **view_params)
def comment(): act = request.args.get('act') or request.json.get('act') if request.method == 'POST': file_id = int(request.json.get('fileId', 0)) else: # it's a GET file_id = int(request.args.get('fileId', 0)) file = SolutionFile.get_or_none(file_id) if file is None: return fail(404, f'No such file {file_id}.') solver_id = file.solution.solver.id if solver_id != current_user.id and not current_user.role.is_manager: return fail(403, "You aren't allowed to access this page.") if act == 'fetch': return jsonify(Comment.by_file(file_id)) if ( not webapp.config.get('USERS_COMMENTS', False) and not current_user.role.is_manager ): return fail(403, "You aren't allowed to access this page.") if act == 'delete': comment_id = int(request.args.get('commentId')) comment_ = Comment.get_or_none(Comment.id == comment_id) if ( comment_.commenter.id != current_user.id and not current_user.role.is_manager ): return fail(403, "You aren't allowed to access this page.") if comment_ is not None: comment_.delete_instance() return jsonify({'success': 'true'}) if act == 'create': kind = request.json.get('kind', '') comment_id, comment_text = None, None try: line_number = int(request.json.get('line', 0)) except ValueError: line_number = 0 if kind.lower() == 'id': comment_id = int(request.json.get('comment', 0)) if kind.lower() == 'text': comment_text = request.json.get('comment', '') return comments._create_comment( current_user.id, file, kind, line_number, comment_text, comment_id, ) return fail(400, f'Unknown or unset act value "{act}".')
def change_last_course_viewed(course_id: int): course = Course.get_or_none(course_id) if course is None: return fail(404, f'No such course {course_id}.') user = User.get(User.id == current_user.id) if not UserCourse.is_user_registered(user.id, course.id): return fail(403, "You're not allowed to access this page.") user.last_course_viewed = course user.save() return redirect(url_for('exercises_page'))
def user(user_id): if user_id != current_user.id and not current_user.role.is_manager: return fail(403, "You aren't allowed to watch this page.") target_user = User.get_or_none(User.id == user_id) if target_user is None: return fail(404, 'There is no such user.') return render_template( 'user.html', solutions=Solution.of_user(target_user.id, with_archived=True), user=target_user, )
def shared_solution(shared_url: str, file_id: Optional[int] = None): if not webapp.config.get('SHAREABLE_SOLUTIONS', False): return fail(404, 'Solutions are not shareable.') shared_solution = SharedSolution.get_or_none( SharedSolution.shared_url == shared_url, ) if shared_solution is None: return fail(404, 'The solution does not exist.') solution_id = shared_solution.solution.id return view( solution_id=solution_id, file_id=file_id, shared_url=shared_url, )
def join_public_course(course_id: int): course = Course.get_or_none(course_id) if course is None: return fail(404, 'There is no such course.') if not course.is_public: return fail(403, "You aren't allowed to do this method.") try: users.join_public_course(course, current_user) except AlreadyExists as e: error_message, status_code = e.args return fail(status_code, error_message) return redirect(url_for('exercises_page'))
def try_or_fail(callback: Callable, *args: Any, **kwargs: Any): try: result = callback(*args, **kwargs) except LmsError as e: error_message, status_code = e.args return fail(status_code, error_message) return result or jsonify({'success': 'true'})
def _create_comment( user_id: int, file: SolutionFile, kind: str, line_number: int, comment_text: Optional[str] = None, # set when kind == text comment_id: Optional[int] = None, # set when kind == id ): user = User.get_or_none(User.id == user_id) if user is None: # should never happen, we checked session_id == solver_id return fail(404, 'No such user.') if (not kind) or (kind not in ('id', 'text')): return fail(400, 'Invalid kind.') if line_number <= 0: return fail(422, f'Invalid line number: {line_number}.') if kind == 'id': new_comment_id = comment_id elif kind == 'text': if not comment_text: return fail(422, 'Empty comments are not allowed.') new_comment_id = CommentText.create_comment(text=comment_text).id else: # should never happend, kind was checked before return fail(400, 'Invalid kind.') solutions.notify_comment_after_check(user, file.solution) comment_ = Comment.create( commenter=user, line_number=line_number, comment=new_comment_id, file=file, ) return jsonify({ 'success': 'true', 'text': comment_.comment.text, 'author_name': user.fullname, 'author_role': user.role.id, 'is_auto': False, 'id': comment_.id, 'line_number': line_number, })
def comment(): act = request.args.get('act') or request.json.get('act') if request.method == 'POST': file_id = int(request.json.get('fileId', 0)) else: # it's a GET file_id = int(request.args.get('fileId', 0)) file = SolutionFile.get_or_none(file_id) if file is None: return fail(404, f'No such file {file_id}.') solver_id = file.solution.solver.id if solver_id != current_user.id and not current_user.role.is_manager: return fail(403, "You aren't allowed to access this page.") if act == 'fetch': return jsonify(Comment.by_file(file_id)) if (not webapp.config.get('USERS_COMMENTS', False) and not current_user.role.is_manager): return fail(403, "You aren't allowed to access this page.") if act == 'delete': return try_or_fail(comments.delete) if act == 'create': user = User.get_or_none(User.id == current_user.id) try: comment_ = comments.create(file=file, user=user) except LmsError as e: error_message, status_code = e.args return fail(status_code, error_message) return jsonify({ 'success': 'true', 'text': comment_.comment.text, 'is_auto': False, 'author_name': user.fullname, 'author_role': user.role.id, 'id': comment_.id, 'line_number': comment_.line_number, }) return fail(400, f'Unknown or unset act value "{act}".')
def user(user_id): if user_id != current_user.id and not current_user.role.is_manager: return fail(403, "You aren't allowed to watch this page.") target_user = User.get_or_none(User.id == user_id) if target_user is None: return fail(404, 'There is no such user.') is_manager = current_user.role.is_manager return render_template( 'user.html', solutions=Solution.of_user( target_user.id, with_archived=True, from_all_courses=True, ), user=target_user, is_manager=is_manager, notes_options=Note.get_note_options(), public_course_exists=Course.public_course_exists(), )
def recover_password(user_id: int, token: str): user = User.get_or_none(User.id == user_id) if user is None: return fail(404, 'The authentication code is invalid.') try: SERIALIZER.loads( token, salt=retrieve_salt(user), max_age=CONFIRMATION_TIME, ) except SignatureExpired: return redirect(url_for( 'login', login_message=( _('Reset password link is expired'), ), )) except BadSignature: return fail(404, 'The authentication code is invalid.') else: return recover_password_check(user, token)
def confirm_email(user_id: int, token: str): user = User.get_or_none(User.id == user_id) if user is None: return fail(404, 'The authentication code is invalid.') if not user.role.is_unverified: return fail(403, 'User has been already confirmed.') try: SERIALIZER.loads( token, salt=retrieve_salt(user), max_age=CONFIRMATION_TIME, ) except SignatureExpired: send_confirmation_mail(user) return redirect(url_for( 'login', login_message=( _( 'The confirmation link is expired, new link has been ' 'sent to your email', ), ), )) except BadSignature: return fail(404, 'The authentication code is invalid.') else: update = User.update( role=Role.get_student_role(), ).where(User.username == user.username) update.execute() return redirect(url_for( 'login', login_message=( _( 'Your user has been successfully confirmed, ' 'you can now login', ), ), ))
def share(): act = request.json.get('act') solution_id = int(request.json.get('solutionId', 0)) try: shared_solution = share_link.get(solution_id) except LmsError as e: error_message, status_code = e.args return fail(status_code, error_message) if act == 'get': return jsonify({ 'success': 'true', 'share_link': shared_solution.shared_url, }) elif act == 'delete': shared_solution.delete_instance() return jsonify({ 'success': 'true', 'share_link': 'false', }) return fail(400, f'Unknown or unset act value "{act}".')
def download(download_id: str): """Downloading a zip file of the code files. Args: download_id (str): Can be on each side of a solution.id and sharedsolution.shared_url. """ try: files, filename = solutions.get_download_data(download_id) except LmsError as e: error_message, status_code = e.args return fail(status_code, error_message) response = make_response(solutions.create_zip_from_solution(files)) response.headers.set('Content-Type', 'zip') response.headers.set( 'Content-Disposition', 'attachment', filename=f'{filename}.zip'.encode('utf-8'), ) return response
def wrapper(*args, **kwargs): if not current_user.role.is_manager: return fail(403, 'This user has no permissions to view this page.') else: return func(*args, **kwargs)
def send(course_id: int, _exercise_number: Optional[int]): if not UserCourse.is_user_registered(current_user.id, course_id): return fail(403, "You aren't allowed to watch this page.") return render_template('upload.html', course_id=course_id)
def send_(course_id: int): if not UserCourse.is_user_registered(current_user.id, course_id): return fail(403, "You aren't allowed to watch this page.") return render_template('upload.html', course_id=course_id)
def get_next_url(url_next_param: Optional[str]): next_url = url_next_param if url_next_param else None if not is_safe_url(next_url): return fail(400, "The URL isn't safe.") return redirect(next_url or url_for('main'))