Loading portal/facade/submissions_facade.py +11 −4 Original line number Diff line number Diff line import logging from portal.database import Submission, SubmissionState, User from portal.database import Project, Submission, SubmissionState, User from portal.facade.general_facade import GeneralCRUDFacade from portal.logger import SUBMIT from portal.service import errors Loading Loading @@ -116,17 +116,24 @@ class SubmissionsFacade(GeneralCRUDFacade): user_ids=user_ids, project_ids=project_ids, role_ids=role_ids, group_ids=group_ids) def is_read_notepad(self, submission: Submission): def is_read_notepad(self, submission: Submission = None, project: Project = None, user: User = None): """Read IS MUNI notepad for the author's submission Args: user(User): User instance project(Project): project instance submission(Submission): Submission instance """ log.debug(f"[IS] Reading notepad content for {submission.log_name} " f"by {self.client_name}") if submission is not None: project = project or submission.project user = user or submission.user note = self._services.is_api(submission.course).read_note( project=submission.project, user=submission.user) project=project, user=user) return note def is_write_notepad(self, submission: Submission, data: dict = None): Loading portal/rest/custom_resource.py +8 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ from flask_restplus import Resource from portal import facade from portal.database.models import Client from portal.service import errors from portal.service.auth import AuthService from portal.service.find import FindService from portal.service.permissions import PermissionsService Loading Loading @@ -119,3 +120,10 @@ class CustomResource(Resource): @property def flask_app(self) -> flask.Flask: return flask.current_app def get_query_param(self, name: str, required=False, as_list=False): method = self.request.args.getlist if as_list else self.request.args.get value = method(name) if not value and required: raise errors.PortalAPIError(message=f"Required query param is missing: {name}") return value portal/rest/error_handlers.py +6 −1 Original line number Diff line number Diff line import flask import portal.storage from flask import Flask from flask_jwt_extended.exceptions import NoAuthorizationError from marshmallow.exceptions import ValidationError from sqlalchemy.exc import SQLAlchemyError import portal.storage from portal import logger from portal.rest import rest_api from portal.service.errors import IncorrectCredentialsError, PortalAPIError, UnauthorizedError Loading Loading @@ -81,3 +81,8 @@ def handle_storage_error(ex: portal.storage.errors.KontrStorageError): def handle_portal_api_error(ex: PortalAPIError): log.error(f"[API] Api error[{ex.code}]: {ex} ") return send_response({'message': ex.message}), ex.code @rest_api.errorhandler(Exception) def handle_default_exception(ex: Exception): log.critical(f"[ERROR] Fatal error: {ex}") portal/rest/projects.py +22 −1 Original line number Diff line number Diff line import flask from flask import request from flask_jwt_extended import jwt_required from flask_restplus import Namespace Loading Loading @@ -158,6 +157,28 @@ class ProjectTestFilesRefresh(CustomResource): return '', 204 @projects_namespace.route('/courses/<string:cid>/projects/<string:pid>/is_muni/notepad') @projects_namespace.param('cid', 'Course id') @projects_namespace.param('pid', 'Project id') @projects_namespace.response(404, 'Course not found') @projects_namespace.response(404, 'Project not found') class ProjectTestFilesRefresh(CustomResource): @jwt_required @access_log @projects_namespace.response(204, 'Project test_files updated') def get(self, cid: str, pid: str): course = self.find.course(cid) project = self.find.project(course, pid) user_id = self.get_query_param('user_id', required=True) user = self.find.user(user_id) # authorization self.permissions(course=course).require.evaluate_submissions() log.debug(f"[REST] Get is muni notes for " f"{project.log_name} by {self.client.log_name}") self.facades.submissions.is_read_notepad(project=project, user=user) return '', 204 @projects_namespace.route( '/courses/<string:cid>/projects/<string:pid>/submissions') @projects_namespace.param('cid', 'Course id') Loading portal/tools/is_api_adapter.py +2 −0 Original line number Diff line number Diff line Loading @@ -51,10 +51,12 @@ class IsApiWrapper: ucos = [user.uco for user in users] if not ucos: return None log.info(f"[IS_API_ADAPT] Read notepad for {shortcut} to user {users}") content = self.is_client.notepad_content(shortcut, *ucos) return content def write_notepad(self, user: 'User', content=None, shortcut: str = None): log.info(f"[IS_API_ADAPT] Write notepad for {shortcut} to user {user.log_name}: {content}") self.is_client.notepad_update(shortcut=shortcut, uco=user.uco, override=True, content=content) Loading Loading
portal/facade/submissions_facade.py +11 −4 Original line number Diff line number Diff line import logging from portal.database import Submission, SubmissionState, User from portal.database import Project, Submission, SubmissionState, User from portal.facade.general_facade import GeneralCRUDFacade from portal.logger import SUBMIT from portal.service import errors Loading Loading @@ -116,17 +116,24 @@ class SubmissionsFacade(GeneralCRUDFacade): user_ids=user_ids, project_ids=project_ids, role_ids=role_ids, group_ids=group_ids) def is_read_notepad(self, submission: Submission): def is_read_notepad(self, submission: Submission = None, project: Project = None, user: User = None): """Read IS MUNI notepad for the author's submission Args: user(User): User instance project(Project): project instance submission(Submission): Submission instance """ log.debug(f"[IS] Reading notepad content for {submission.log_name} " f"by {self.client_name}") if submission is not None: project = project or submission.project user = user or submission.user note = self._services.is_api(submission.course).read_note( project=submission.project, user=submission.user) project=project, user=user) return note def is_write_notepad(self, submission: Submission, data: dict = None): Loading
portal/rest/custom_resource.py +8 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ from flask_restplus import Resource from portal import facade from portal.database.models import Client from portal.service import errors from portal.service.auth import AuthService from portal.service.find import FindService from portal.service.permissions import PermissionsService Loading Loading @@ -119,3 +120,10 @@ class CustomResource(Resource): @property def flask_app(self) -> flask.Flask: return flask.current_app def get_query_param(self, name: str, required=False, as_list=False): method = self.request.args.getlist if as_list else self.request.args.get value = method(name) if not value and required: raise errors.PortalAPIError(message=f"Required query param is missing: {name}") return value
portal/rest/error_handlers.py +6 −1 Original line number Diff line number Diff line import flask import portal.storage from flask import Flask from flask_jwt_extended.exceptions import NoAuthorizationError from marshmallow.exceptions import ValidationError from sqlalchemy.exc import SQLAlchemyError import portal.storage from portal import logger from portal.rest import rest_api from portal.service.errors import IncorrectCredentialsError, PortalAPIError, UnauthorizedError Loading Loading @@ -81,3 +81,8 @@ def handle_storage_error(ex: portal.storage.errors.KontrStorageError): def handle_portal_api_error(ex: PortalAPIError): log.error(f"[API] Api error[{ex.code}]: {ex} ") return send_response({'message': ex.message}), ex.code @rest_api.errorhandler(Exception) def handle_default_exception(ex: Exception): log.critical(f"[ERROR] Fatal error: {ex}")
portal/rest/projects.py +22 −1 Original line number Diff line number Diff line import flask from flask import request from flask_jwt_extended import jwt_required from flask_restplus import Namespace Loading Loading @@ -158,6 +157,28 @@ class ProjectTestFilesRefresh(CustomResource): return '', 204 @projects_namespace.route('/courses/<string:cid>/projects/<string:pid>/is_muni/notepad') @projects_namespace.param('cid', 'Course id') @projects_namespace.param('pid', 'Project id') @projects_namespace.response(404, 'Course not found') @projects_namespace.response(404, 'Project not found') class ProjectTestFilesRefresh(CustomResource): @jwt_required @access_log @projects_namespace.response(204, 'Project test_files updated') def get(self, cid: str, pid: str): course = self.find.course(cid) project = self.find.project(course, pid) user_id = self.get_query_param('user_id', required=True) user = self.find.user(user_id) # authorization self.permissions(course=course).require.evaluate_submissions() log.debug(f"[REST] Get is muni notes for " f"{project.log_name} by {self.client.log_name}") self.facades.submissions.is_read_notepad(project=project, user=user) return '', 204 @projects_namespace.route( '/courses/<string:cid>/projects/<string:pid>/submissions') @projects_namespace.param('cid', 'Course id') Loading
portal/tools/is_api_adapter.py +2 −0 Original line number Diff line number Diff line Loading @@ -51,10 +51,12 @@ class IsApiWrapper: ucos = [user.uco for user in users] if not ucos: return None log.info(f"[IS_API_ADAPT] Read notepad for {shortcut} to user {users}") content = self.is_client.notepad_content(shortcut, *ucos) return content def write_notepad(self, user: 'User', content=None, shortcut: str = None): log.info(f"[IS_API_ADAPT] Write notepad for {shortcut} to user {user.log_name}: {content}") self.is_client.notepad_update(shortcut=shortcut, uco=user.uco, override=True, content=content) Loading