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 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() })
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())
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)})
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 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})
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 '' })
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({})
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())