class UploadUrlHandler(ApiRequestHandler):
    path = resource.endpoint('/dashboard/uploadurl/repository/<studentId>')

    @path.operation(type_='BlobStoreUploadInfo',
                    alias='newUploadUrl',
                    parameters=[
                        swagger.String(
                            name="studentId",
                            param_type="path",
                            description="Id of student details to edit",
                            required=True)
                    ],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden")
                    ])
    def post(self, studentId):
        """Create a new blobstore upload url.

        The student id is currently not used with this implementation.
        The student id should be sent with uploaded file.

        """
        self.staff_required()
        self.render_json(
            {"url": blobstore.create_upload_url(config.UPLOAD_CB_URL)})
Beispiel #2
0
class UserApi(ApiRequestHandler):
    """Handler requests for a specific user.

    """
    path = student_resource.endpoint("/dashboard/users/<userId>")

    @path.operation(type_="User",
                    alias="deleteUser",
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(400, "Bad Request"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                        swagger.Message(404, "Not Found"),
                    ])
    def delete(self, userId):
        """Delete a user.

        Note that it doesn't prevent a user from registering again.

        """
        current_user = self.admin_required()
        if current_user.key.id() == userId:
            self.abort(400)

        user = models.User.get_by_id(userId)
        if user is None:
            self.abort(404)

        user.key.delete()
        self.render_json({})
class RepositoryDocumentListApi(ApiRequestHandler):
    """Handle Document listing requests

    """
    path = resource.endpoint('/dashboard/repository/<studentId>/files')

    @path.operation(
        type_="DocumentList",
        alias="getRepositoryByStudentId",
        parameters=[
            swagger.String(name="cursor",
                           description="Cursor to query the next page",
                           param_type="query"),
            swagger.String(name="studentId",
                           param_type="path",
                           description="Id of student details to edit",
                           required=True)
        ],
        responses=[
            swagger.Message(200, "Ok"),
            swagger.Message(400, "Bad Request"),
            swagger.Message(401, "Unauthorized"),
            swagger.Message(403, "Forbidden"),
            swagger.Message(404, "Not Found")
        ])
    def get(self, studentId):
        """List all the files at destination to a specific student

        """
        student = Student.get_by_id(studentId)
        if not student:
            self.abort(404)

        current_user = self.login_required()
        if (not current_user.is_staff and not current_user.is_admin
                and not current_user.is_domain_admin
                and current_user.student_id != student.key.id()):
            self.abort(403)
            return

        cursor_key = self.request.GET.get('cursor')

        # using cheap request and ndb entities cache
        file_keys, cursor, _ = models.Document.get_files(student.key,
                                                         cursor_key,
                                                         keys_only=True)
        ffiles = [k.get_async() for k in file_keys]

        self.render_json({
            'files':
            map(self.file_dict, [ff.get_result() for ff in ffiles]),
            'cursor':
            cursor.urlsafe() if cursor else ''
        })

    def file_dict(self, file_):
        data = file_.summary()
        data['url'] = self.uri_for('dashboard_download_file',
                                   keyId=file_.key.id())
        return data
class AssessmentExamApi(ApiRequestHandler):
    """Handle request for an exam resource

    """
    path = assessment_resource.endpoint(
        '/dashboard/assessments/exams/<examId:\d+>')

    @path.operation(type_='AssessmentExam',
                    alias='getExamDetails',
                    parameters=[
                        swagger.String(
                            name="examId",
                            description="Id of exam to show details for",
                            param_type="path",
                            required=True),
                    ],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                        swagger.Message(404, "Not Found")
                    ])
    def get(self, examId):
        """Retrieve detailed informations about an exam

        """
        self.staff_required()

        exam = models.AssessmentExam.get_by_id(int(examId))
        if exam is None:
            self.abort(404)
        self.render_json(exam.details())
Beispiel #5
0
class UserListApi(ApiRequestHandler):
    """Handle user list resource.

    """
    path = student_resource.endpoint("/dashboard/users")

    @path.operation(type_="UserList",
                    alias="listUsers",
                    parameters=[
                        swagger.String(
                            name="cursor",
                            description="Cursor to query the next page",
                            param_type="query")
                    ],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                    ])
    def get(self):
        """List all Users (20 per page).

        The current user must be logged in as staff to see
        the list of users.

        """
        self.staff_required()

        cursor_key = self.request.GET.get('cursor')
        users, cursor = models.User.get_users(cursor_key)
        return self.render_json({
            'type': 'users',
            'users': [s.summary() for s in users],
            'cursor': cursor if cursor else ''
        })
class AssessmentExamListApi(ApiRequestHandler):
    """Handle operations on the exam lists

    """
    path = assessment_resource.endpoint('/dashboard/assessments/exams')

    @path.operation(
        type_='AssessmentExamList',
        alias='listExams',
        parameters=[
            swagger.String(
                name="studentId",
                description="Id of student details to list exam for",
                param_type="query"),
            swagger.String(name="cursor",
                           description="Cursor to query the next page",
                           param_type="query"),
        ],
        responses=[
            swagger.Message(200, "Ok"),
            swagger.Message(401, "Unauthorized"),
            swagger.Message(403, "Forbidden"),
            swagger.Message(404, "Not Found")
        ])
    def get(self):
        """Return a list exams.

        """
        student_id = self.request.GET.get('studentId')
        if student_id:
            self._get_exams_by_student_id(student_id)
        else:
            self._get_exams()

    def _get_exams(self):
        self.staff_required()

        exams = models.AssessmentExam.get_exams()
        self.render_json({
            'cursor': '',
            'exams': [e.summary() for e in exams],
        })

    def _get_exams_by_student_id(self, student_id):
        student = Student.get_by_id(student_id)
        if not student:
            self.abort(404, 'User not found')

        current_user = self.login_required()
        if (not current_user.is_staff and not current_user.is_admin
                and not current_user.is_domain_admin
                and student.key.id() != current_user.student_id):
            self.abort(403)

        exams = models.AssessmentExam.get_by_student_id(student.key.id())
        self.render_json({
            'cursor': '',
            'exams': [e.summary() for e in exams],
            'student': student.summary()
        })
Beispiel #7
0
class StudentNameApi(ApiRequestHandler):
    """Handle request for a specific student.

    """
    path = student_resource.endpoint(
        "/dashboard/students/<studentId>/<propName>")

    @path.operation(type_="Student",
                    alias="saveStudentName",
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                        swagger.Message(404, "Not Found"),
                    ])
    def put(self, studentId, propName):
        """Save student name.

        """
        self.staff_required()
        methods = {
            'name': models.Student.edit_name,
            'email': models.Student.edit_email,
            'year': models.Student.edit_year,
        }

        if propName not in methods:
            self.abort(404)

        try:
            payload = json.loads(self.request.body)
            student = methods[propName](studentId, payload)
        except (
                ValidationError,
                ValueError,
                AttributeError,
        ):
            self.abort(400)

        if student is None:
            self.abort(404)

        StudentListApi.reset_list_cache()
        self.render_json({})
Beispiel #8
0
class StudentProfileUploadUrlApi(ApiRequestHandler):
    path = student_resource.endpoint('/dashboard/uploadurl/studentsprofile')

    @path.operation(type_='BlobStoreUploadInfo',
                    alias='newStudentUploadUrl',
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden")
                    ])
    def post(self):
        """Create a new blobstore upload url for a student list.

        """
        self.admin_required()
        self.render_json(
            {"url": blobstore.create_upload_url(config.PHOTO_CB_URL)})
class AssessmentUploadUrlHandler(ApiRequestHandler):
    path = assessment_resource.endpoint('/dashboard/uploadurl/assessments')

    @path.operation(type_='BlobStoreUploadInfo',
                    alias='newExamUploadUrl',
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden")
                    ])
    def post(self):
        """Create a new blobstore upload url.

        """
        self.admin_required()
        self.render_json(
            {"url": blobstore.create_upload_url(config.UPLOAD_EXAM_URL)})
Beispiel #10
0
class CurrentUserApi(ApiRequestHandler):
    """Handler request on user login status

    """
    resource = api.resource(
        path="/user", desc="Operations about current user authentication")
    path = resource.endpoint('/dashboard/user')

    @path.operation(type_="User",
                    alias="isloggedIn",
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                    ])
    def get(self):
        """Return the user info if logged in or the url to login
        if the user is logged off.

        TODO: get and use user info instead of user nickname

        """
        user = self.get_current_user()
        return_url = self.request.GET.get('returnUrl',
                                          config.DEFAULT_RETURN_URL)
        return_url = urllib.unquote(return_url)

        signed_return_url = utils.sign_return_url(return_url,
                                                  self.session_id())

        login_url = '%s?state=%s' % (
            config.LOGIN_URL,
            signed_return_url,
        )
        logout_url = '%s?state=%s' % (
            config.LOGOUT_URL,
            signed_return_url,
        )

        if not user:
            self.render_json({
                'isStudent': False,
                'isStaff': False,
                'isAdmin': False,
                'isLoggedIn': False,
                'loginUrl': login_url,
            })
            return

        resp = user.details()
        resp['isLoggedIn'] = True
        resp['loginUrl'] = login_url
        resp['logoutUrl'] = logout_url

        self.render_json(resp)
Beispiel #11
0
class RepositoryDocumentApi(ApiRequestHandler):
    """Handle requests on a document

    """

    path = resource.endpoint(
        '/dashboard/repository/<studentId>/files/<fileId>')

    @path.operation(
        type_="DocumentList",
        alias="deleteDocument",
        parameters=[
            swagger.String(name="studentId",
                           param_type="path",
                           description="Id of student details to edit",
                           required=True),
            swagger.String(name="fileId",
                           param_type="path",
                           description="Id of the document to delete",
                           required=True)
        ],
        responses=[
            swagger.Message(200, "Ok"),
            swagger.Message(401, "Unauthorized"),
            swagger.Message(403, "Forbidden"),
            swagger.Message(404, "Not Found")
        ])
    def delete(self, studentId, fileId):
        """Delete a document.

        """
        self.admin_required()

        student = Student.get_by_id(studentId)
        document = models.Document.get_by_id(fileId)
        if (not student or not document
                or document.dest_ref.id() != student.key.id()):
            self.abort(404)

        document.delete()
        self.render_json({'success': True})
Beispiel #12
0
class StudentUploadResultApi(ApiRequestHandler):
    path = student_resource.endpoint('/dashboard/uploadjob/students/<jobId>')

    @path.operation(type_='JobResult',
                    alias='getJobStatus',
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden")
                    ])
    def post(self, jobId):
        """Create a new blobstore upload url for a student list.

        """
        self.staff_required()
        pipeline = ProcessNewStudent.from_id(jobId)
        if pipeline is None:
            self.abort(404)

        self.render_json({'id': jobId, 'completed': pipeline.has_finalized()})
Beispiel #13
0
class RoshReviewUserStatsApi(ApiRequestHandler):
    """Handle request for a student Rosh Review stats.

    """

    path = roshreview_resource.endpoint(
        '/dashboard/roshreview/stats/<studentId>')

    @path.operation(
        type_='RoshReviewUserStats',
        alias='getRoshReviewStats',
        # TODO: add filters
        parameters=[],
        responses=[
            swagger.Message(200, "Ok"),
            swagger.Message(401, "Unauthorized"),
            swagger.Message(403, "Forbidden"),
            swagger.Message(404, "Not Found")
        ])
    def get(self, studentId):
        """Get the detailed stats for a student.

        """
        student_id = studentId.upper()
        current_user = self.login_required()
        if current_user.student_id != student_id:
            self.staff_required()

        stats = RoshReviewUserStats.get_by_id(student_id)
        if stats is None:
            self.abort(404)
        details = stats.details()
        today = datetime.date.today()

        details['history'] = [{
            'performance':
            0,
            'date': (today - datetime.timedelta(days=i)).isoformat()
        } for i in reversed(range(183))]
        self.render_json(details)
Beispiel #14
0
class StudentApi(ApiRequestHandler):
    """Handle request for a specific student.

    """
    path = student_resource.endpoint("/dashboard/students/<studentId>")

    @path.operation(type_="Student",
                    alias="getStudent",
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                        swagger.Message(404, "Not Found"),
                    ])
    def get(self, studentId):
        """Return the student details.

        """
        self.staff_required()
        student = models.Student.get_by_id(studentId)
        if student is None:
            self.abort(404)

        self.render_json(student.details())

    @path.operation(type_="Student",
                    alias="deleteStudent",
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                        swagger.Message(404, "Not Found"),
                    ])
    def delete(self, studentId):
        """Delete a student.

        """
        self.staff_required()

        student = models.Student.get_by_id(studentId)
        if student is None:
            self.abort(404)

        student.key.delete()
        StudentListApi.reset_list_cache()
        self.render_json({})
Beispiel #15
0
class FirstAidTopicListApi(ApiRequestHandler):
    """Handle request on Topic list resource

    """
    path = firstaid_resource.endpoint('/dashboard/firstaid/topics')

    @path.operation(type_="TopicList",
                    alias="listFirstAidTopics",
                    parameters=[],
                    responses=[swagger.Message(200, "Ok")])
    def get(self):
        """Return the list of topic.

        """
        self.response.cache_control = 'public'
        self.response.cache_control.max_age = 300
        self.render_json({
            "type":
            "topics",
            "topics": [t.summary() for t in FirstAidUserStats.get_topics()]
        })
Beispiel #16
0
class PGYListApi(ApiRequestHandler):
    """Handle request of PGY list resource

    """
    path = pgy_resource.endpoint('/dashboard/pgy')

    @path.operation(type_="PGYList",
                    alias="listPgy",
                    parameters=[],
                    responses=[swagger.Message(200, "Ok")])
    def get(self):
        """Return the list of PGY.

        """
        self.response.cache_control = 'public'
        self.response.cache_control.max_age = 300
        self.render_json({
            "pgy": [{
                "id": year.year,
                "label": "Year %s" % year.year,
                "isActive": year.is_active
            } for year in models.Student.get_pgys()]
        })
Beispiel #17
0
class TopicListApi(ApiRequestHandler):
    """Handle request of PGY list resource

    """
    path = roshreview_resource.endpoint('/dashboard/roshreview/topic')

    @path.operation(type_="TopicList",
                    alias="listReviewTopics",
                    parameters=[],
                    responses=[swagger.Message(200, "Ok")])
    def get(self):
        """Return the list of PGY.

        """
        self.response.cache_control = 'public'
        self.response.cache_control.max_age = 300
        self.render_json({
            "type":
            "topics",
            "topics": [{
                "id": topic,
                "label": topic.title()
            } for topic in RoshReviewUserStats.get_topics()]
        })
Beispiel #18
0
class FirstAidStatsApi(ApiRequestHandler):
    """Handle request for First Aid stats listing.

    """

    path = firstaid_resource.endpoint('/dashboard/firstaid/stats')

    @path.operation(
        type_='FirstAidUserStatsList',
        alias='listFirstAidStats',
        # TODO: add filters
        parameters=[
            swagger.String(name="cursor",
                           description="Cursor to query the next page",
                           param_type="query")
        ],
        responses=[
            swagger.Message(200, "Ok"),
            swagger.Message(401, "Unauthorized"),
            swagger.Message(403, "Forbidden"),
            swagger.Message(404, "Not Found")
        ])
    def get(self):
        """List student stats.

        """
        self.staff_required()
        cursor_key = self.request.GET.get('cursor')
        topic_id = self.request.GET.get('topic')
        sort_by = self.request.GET.get('sortBy')

        if topic_id == 'all':
            topic_id = None

        sort_by_options = {
            'performance': 'performance',
            'questionTaken': 'question_taken'
        }
        sort_by = sort_by_options.get(sort_by, 'performance')

        try:
            limit = int(self.request.GET.get('limit'))
        except (
                ValueError,
                TypeError,
        ):
            limit = None

        try:
            residents = self.request.GET.get('residents')
            if residents == 'all':
                residents = None
            else:
                residents = int(residents)
        except (
                ValueError,
                TypeError,
        ):
            residents = None

        stats, cursor = FirstAidUserStats.get_stats(cursor_key=cursor_key,
                                                    limit=limit,
                                                    year=residents,
                                                    topic_id=topic_id,
                                                    sort_by=sort_by)
        self.render_json({
            'stats': [s.summary() for s in stats],
            'cursor': cursor if cursor else ''
        })
Beispiel #19
0
class AdminApi(ApiRequestHandler):
    """Handle request on a admin user

    """
    path = admin_resource.endpoint("/dashboard/admin/<userId:\d+>")

    @path.operation(type_="User",
                    alias="makeAdmin",
                    parameters=[
                        swagger.String(name="userId",
                                       param_type="path",
                                       description="Id of user to make admin",
                                       required=True)
                    ],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                        swagger.Message(404, "Not Found"),
                    ])
    def put(self, userId):
        """Flag a user as an staff.

        """
        self.admin_required()
        user_id = int(userId)
        user = models.User.get_by_id(user_id)

        if user is None:
            self.abort(404)

        models.User.make_admin(user_id)
        self.render_json({})

    @path.operation(
        type_="User",
        alias="revokeAdmin",
        parameters=[
            swagger.String(
                name="userId",
                param_type="path",
                description="Id of user to revoke admin permission from",
                required=True)
        ],
        responses=[
            swagger.Message(200, "Ok"),
            swagger.Message(401, "Unauthorized"),
            swagger.Message(403, "Forbidden"),
            swagger.Message(404, "Not Found"),
        ])
    def delete(self, userId):
        """Remove staff flag from a user.

        """
        self.admin_required()
        user_id = int(userId)
        user = models.User.get_by_id(user_id)

        if user is None:
            self.abort(404)

        if user.is_domain_admin:
            self.abort(400)

        models.User.revoke_admin(user_id)
        self.render_json({})
Beispiel #20
0
class StudentListApi(ApiRequestHandler):
    """Handle student list resource.

    """
    path = student_resource.endpoint("/dashboard/students")

    @path.operation(type_="StudentList",
                    alias="listStudents",
                    parameters=[
                        swagger.String(
                            name="cursor",
                            description="Cursor to query the next page",
                            param_type="query")
                    ],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                    ])
    def get(self):
        """List all students (20 per page).

        The current user must be logged in as an app admin to see
        the list of student.

        """
        self.staff_required()

        cursor_key = self.request.GET.get('cursor')
        limit = self.request.GET.get('limit')
        name = self.request.GET.get('name', '')
        raw_years = self.request.GET.getall('years')

        if limit is not None:
            try:
                limit = int(limit)
            except (ValueError, TypeError):
                limit = None

        years = []
        for y in raw_years:
            try:
                years.append(int(y))
            except (
                    ValueError,
                    TypeError,
            ):
                pass

        props = {
            "limit": limit,
            "name": name.lower(),
            "years": years,
            "cursor_key": cursor_key
        }

        if limit is 0:
            students = self.get_list()
            cursor = None
        else:
            students, cursor = models.Student.get_students(**props)

        return self.render_json({
            'type': 'students',
            'students': students,
            'cursor': cursor if cursor else ''
        })

    cache_ttl = 60 * 60

    @staticmethod
    def cache_key():
        return 'OEPSTUDENT_STUDENT_LIST'

    @classmethod
    def get_list(cls, **kw):
        key = cls.cache_key()
        cache = memcache.get(key)
        if cache is not None:
            return cache

        students, _ = models.Student.get_students(cursor_key=None, limit=0)
        memcache.set(key, students, time=cls.cache_ttl)

        return students

    @classmethod
    def reset_list_cache(cls):
        memcache.delete(cls.cache_key())

    @path.operation(type_="Student",
                    alias="newStudent",
                    parameters=[],
                    responses=[
                        swagger.Message(200, "Ok"),
                        swagger.Message(400, "Bad Request"),
                        swagger.Message(401, "Unauthorized"),
                        swagger.Message(403, "Forbidden"),
                    ])
    def post(self):
        """Create a new Student

        """
        self.admin_required()
        try:
            payload = json.loads(self.request.body)
            student = models.Student.new_student(
                payload['name']['givenName'],
                payload['displayName'],
                family_name=payload['name']['familyName'],
                email=payload['secondaryEmail'],
                student_id=payload['studentId'],
                year=payload['year'])
        except (ValidationError, ValueError, AttributeError, KeyError):
            self.abort(400)

        return self.render_json(student.details())