def updatePassword(): # Takes in a json of the form {email : '', password : ''} # Validate that the user calling this has access # Either that they are the same user or that they are an admin if request.json is None: abort(400) for x in ['email', 'password']: if x not in request.json: abort(400) emailSess = mailsane.normalize(session['email']) if emailSess.error: abort(400) email = mailsane.normalize(request.json['email']) if email.error: abort(400) if dbworker.validateAccess(dbworker.userTypeMap['admin']): pass else: abort(401) if dbworker.getUser(str(email)) is None: abort(404) dbworker.setPassword(str(email), request.json['password']) return jsonify({'success': True})
def createUser(): """ Takes in a JSON of the structure { "email": "*****@*****.**", "password": "******", "userType": 1, "firstName": "Test", "lastName": "Admin", "phoneNumber": "555-555-5555", "birthday": "YYYY-MM-DD", "parentEmail" : "", "parentName" : "" } Returns {'success' : Boolean} """ if not dbworker.validateAccess(dbworker.userTypeMap['admin']): abort(403) if request.json is None: abort(400) for x in [ 'email', 'password', 'userType', 'firstName', 'lastName', 'phoneNumber', 'birthday', 'parentEmail', 'parentName' ]: if x not in request.json: abort(400) email = mailsane.normalize(request.json['email']) if email.error: abort(400) # Verify no duplicate email here or in the dbworker method # likely better to do it there parentEmail = mailsane.normalize(request.json['parentEmail']) if parentEmail.error: abort(400) try: validate(instance=request.json, schema=SchemaFactory.create_user) except exceptions.ValidationError: abort(400) if dbworker.getUser(str(email)) is not None: abort(400) dbworker.createUser( str(email), str(parentEmail), request.json['firstName'], request.json['lastName'], request.json['password'], request.json['userType'], request.json['phoneNumber'], datetime.datetime.strptime(request.json['birthday'], '%Y-%m-%d'), request.json['parentName']) return jsonify({'success': True})
def getStudentDashboardInfo(): if 'email' not in session or session['email'] is None: abort(401) email = mailsane.normalize(session['email']) if email.error: abort(400) studentDashboardDict = {} classes = dbworker.getClasses(str(email), filt={'ongoing': True}) thisStudent = dbworker.getCurrentUser() studentDashboardDict['firstName'] = thisStudent['firstName'][:] studentDashboardDict['lastName'] = thisStudent['lastName'][:] studentDashboardDict['Classes'] = [] studentDashboardDict[ 'Classes'] = studentDashboardDict['Classes'] + classes['student'] classReports = dbworker.mclient[dbworker.database]['reports'] for c in studentDashboardDict['Classes']: foundClass = False r = 0 while not foundClass and r < classReports.size(): if classReports[r]['classId'] == c['id']: foundClass = True else: r += 1 if foundClass: c['nextCourse'] = classReports[r]['nextCourse'] c['marks'] = classReports[r]['marks'] return jsonify(studentDashboardDict)
def updateReport(): """ Takes in a json of the form {'classId' : '123', 'email' : student_email, 'mark' : 90.00, 'comment' : "Great!"} Returns a "success" json """ if request.json is None: abort(400) try: validate(instance=request.json, schema=SchemaFactory.report_update) except exceptions.ValidationError: abort(400) if not dbworker.validateAccessList( [dbworker.userTypeMap['admin'], dbworker.userTypeMap['instructor']]): abort(403) studentEmail = mailsane.normalize(request.json['email']) if studentEmail.error: abort(400) convClassId = ObjectId(request.json['classId']) dbworker.updateReport( convClassId, str(studentEmail), mark={} if 'mark' not in request.json else request.json['mark'], comments='' if 'comments' not in request.json else request.json['comments'], nextCourse='' if 'nextCourse' not in request.json else request.json['nextCourse']) return jsonify({'success': True})
def getUserClasses(email): if not dbworker.validateAccessList([ dbworker.userTypeMap['admin'], dbworker.userTypeMap['instructor'], dbworker.userTypeMap['student'] ]): abort(403) email = mailsane.normalize(email) if email.error: abort(400) classes = {'instructor': [], 'student': []} for i in dbworker.mclient[dbworker.database]['classes'].find( {'instructors': str(email)}): tmp_id = i['_id'] classes['instructor'].append({ "id": str(tmp_id), "name": i['courseTitle'], "ongoing": i['ongoing'] }) for j in dbworker.mclient[dbworker.database]['classes'].find( {"students": str(email)}): tmp_id = j['_id'] classes['student'].append({ "id": str(tmp_id), "name": j['courseTitle'], "ongoing": j['ongoing'] }) return jsonify(classes)
def removeStudent(): """ Takes in a JSON of the structure {'email', 'classId'} Removes <email> from <classId> as a student Returns {'success' : Boolean} """ if not dbworker.validateAccess(dbworker.userTypeMap['admin']): abort(403) if request.json is None or 'email' not in request.json or 'classId' not in request.json: abort(400) email = mailsane.normalize(request.json['email']) if email.error: abort(400) convClassId = ObjectId(request.json['classId']) try: validate(instance=request.json, schema=SchemaFactory.move_user) except exceptions.ValidationError: abort(400) us = dbworker.getUser(str(email)) cl = dbworker.getClass(convClassId) if us is None or cl is None: abort(404) if us['userType'] not in [dbworker.userTypeMap['student']]: abort(400) return jsonify( {'success': dbworker.removeStudent(convClassId, str(email))})
def deleteMarkingSection(): """ Takes in a JSON of the following format {classId, sectionTitle} Returns {success : Boolean} Deletes mark weights and marks for sectionTitle in <classId> """ # Validate credentials here if 'email' not in session or session['email'] is None: abort(401) email = mailsane.normalize(session['email']) if email.error: abort(400) if request.json is None: abort(400) for x in ['classId', 'sectionTitle']: if x not in request.json: abort(400) convClassId = ObjectId(request.json['classId']) if not dbworker.validateAccess( dbworker.userTypeMap['admin']) and not dbworker.isClassInstructor( str(email), convClassId): abort(401) dbworker.deleteMarkingSection(convClassId, request.json['sectionTitle']) return jsonify({'success': True})
def getMyMarks(): """ Gets a student's marks If the logged in user is not a student, then it will return a 403 Returned structure is {marks : {}, success : Boolean} The keys for marks and markingSections will be class _ids """ if not dbworker.validateAccess(dbworker.userTypeMap['student']): abort(403) email = mailsane.normalize(session['email']) if email.error: abort(400) marks = dbworker.getReports({'studentEmail': str(email)}) classList = [] marksDict = {} for m in marks: # This is to hide the internal report _ids m.pop('_id', None) tmpId = m['classId'] m.pop('classId', None) # This has to be done as ObjectIds not serializable m.pop('studentEmail', None) classList.append(tmpId) marksDict[str(tmpId)] = m markingSections = dbworker.getMarkingSectionInformation( filt={'_id': { '$in': classList }}) for cl in classList: stredCl = str(cl) tmp = {} for sectionTitle in markingSections[stredCl]: tmp[sectionTitle] = {} tmp[sectionTitle]['weight'] = markingSections[stredCl][ sectionTitle]['weight'] tmp[sectionTitle]['index'] = markingSections[stredCl][ sectionTitle]['index'] if sectionTitle in marksDict[stredCl]['marks']: # This is to handle the case where a 'None' mark exists tmp[sectionTitle]['mark'] = marksDict[stredCl]['marks'][ sectionTitle] else: tmp[sectionTitle]['mark'] = None marksDict[stredCl]['marks'] = tmp return jsonify({'marks': marksDict, 'success': True})
def getCurrentUser(): """ Get the current user associated with whatever email is stored in session """ if 'email' not in session: return None email = mailsane.normalize(session['email']) if email.error: return None return getUser(str(email))
def getClasses(): """ Returns a list of class ids from the database """ if 'email' not in session or session['email'] is None: abort(401) email = mailsane.normalize(session['email']) if email.error: abort(400) return jsonify({ 'classList': dbworker.getClasses(str(email)), 'success': True })
def addSampleUser(username): if not ENABLE_DEBUG_ROUTES: abort(404) email = mailsane.normalize(username + '@mcode.club') if email.error: abort(400) if dbworker.getUser(str(username + '@mcode.club')) is not None: abort(400) dbworker.createUser(str(email), str(email), 'Sample', 'User', 'I love rock and roll', 1, '647-111-1111', datetime.datetime.strptime('1970-01-01', '%Y-%m-%d'), 'Parent Name') return username
def getStudentReport(class_id, email): """ Return a report for a student for a specific class. Expected json is {"email": [email protected], "classId":"5e5ab2f6e7179a5e7ee4e81b"} """ # try: # validate(instance={"email":email, "classId":class_id}, schema=SchemaFactory.report_student) # except exceptions.ValidationError: # abort(400) email = mailsane.normalize(email) if email.error: abort(400) if not dbworker.validateAccessList( [dbworker.userTypeMap['admin'], dbworker.userTypeMap['instructor']]): abort(403) # Must first convert classId string in to a ObjectId before executing query convClassId = ObjectId(class_id) # Verify: 'email' is an existing user in DB and 'convClassId' is the idea of an existing class us = dbworker.getUser(str(email)) cl = dbworker.getClass(convClassId) if us is None or cl is None: abort(404) if us['userType'] != dbworker.userTypeMap['student']: abort(400) filt = {"classId": convClassId, "studentEmail": str(email)} proj = {'_id': 0} report = dbworker.getStudentReport(filt=filt, proj=proj) if report is None: abort(400) # Must convert ObjectId 'classId' into a string before responding report['classId'] = str(report['classId']) return jsonify({"report": report})
def updateCourseInfo(): """ Takes in a JSON of the following format {classId, status : Boolean, newTitle : String} Returns {success : Boolean} Sets the <ongoing> of classId to <status>, and <courseTitle> to <newTitle> If <semesterInfo> is in request.json, it will update <semester> to <semesterInfo> """ # Validate credentials here if 'email' not in session or session['email'] is None: abort(403) email = mailsane.normalize(session['email']) if email.error: abort(400) if request.json is None or 'classId' not in request.json or 'status' not in request.json or 'newTitle' not in request.json: abort(400) convClassId = ObjectId(request.json['classId']) if not dbworker.validateAccess( dbworker.userTypeMap['admin']) and not dbworker.isClassInstructor( str(email), convClassId): abort(401) try: validate(instance=request.json, schema=SchemaFactory.update_CI) except exceptions.ValidationError: abort(400) json = { 'ongoing': request.json['status'], 'courseTitle': request.json['newTitle'] } if 'semesterInfo' in request.json: json['semester'] = request.json['semesterInfo'] dbworker.updateClassInfo(convClassId, json) return jsonify({'success': True})
def setMarkingSection(): """ Takes in a JSON of the following format {classId, sectionTitle, weightInfo : JSON} weightInfo will be of the form {'weight' : Int, 'index' : Int} Returns {success : Boolean} Sets the weight of sectionTitle in classId to <weight> This will override existing values """ if request.json is None or 'classId' not in request.json or 'sectionTitle' not in request.json or 'weightInfo' not in request.json: abort(400) for x in ['weight', 'index']: if x not in request.json['weightInfo']: abort(400) # Validate credentials here if 'email' not in session or session['email'] is None: abort(401) email = mailsane.normalize(session['email']) if email.error: abort(400) try: validate(instance=request.json, schema=SchemaFactory.set_marking) except exceptions.ValidationError: abort(400) convClassId = ObjectId(request.json['classId']) if not dbworker.validateAccess( dbworker.userTypeMap['admin']) and not dbworker.isClassInstructor( str(email), convClassId): abort(401) dbworker.addMarkingSection(convClassId, request.json['sectionTitle'], request.json['weightInfo']) return jsonify({'success': True})
def genHours(): # request.json['hours'] is currently a string that gets converted server side valid_access = [ dbworker.userTypeMap['admin'], dbworker.userTypeMap['instructor'], dbworker.userTypeMap['volunteer'] ] if not dbworker.validateAccessList(valid_access): abort(403) if request.json is None: abort(400) for x in ['purpose', 'paid', 'hours', 'dateTime']: if x not in request.json: abort(400) correctedTime = datetime.datetime.strptime(request.json['dateTime'], "%Y-%m-%dT%H:%M:%S.%fZ") email = session['email'] if 'email' in request.json: email = mailsane.normalize(request.json['email']) if email.error: abort(400) hours = 0 try: # Handle conversion from a string to a float hours = float(request.json['hours']) except: abort(400) if hours <= 0: abort(400) dbworker.addHoursLog(str(email), request.json['purpose'], request.json['paid'], correctedTime, hours) return jsonify({'success': True})
def validateAccessList(expectedUserTypes): """ Validate that the user is logged in, use the information in the session data to determine if their username is valid and one of the expectedUserTypes, return boolean, True if valid, False if invalid """ if 'email' not in session or session['email'] is None: return False email = mailsane.normalize(session['email']) if email.error: return False uType = getUserType(str(email)) for x in expectedUserTypes: if uType == x: return True return False
def getHours(): if not dbworker.validateAccessList([ dbworker.userTypeMap['admin'], dbworker.userTypeMap['instructor'], dbworker.userTypeMap['volunteer'] ]): abort(403) pre_email = request.args.get('user', default=None, type=str) email = None if pre_email is None: email = session.get('email') if email is None: abort(500) else: if not dbworker.validateAccessList([dbworker.userTypeMap['admin']]): abort(403) email = mailsane.normalize(pre_email) if email.error: abort(400) hours = dbworker.getHours(filt={"email": str(email)}, projection={ '_id': 1, 'dateTime': 1, 'purpose': 1, 'hours': 1, 'paid': 1 }) hours_list = [] for doc in hours: doc['_id'] = str(doc['_id']) hours_list.append(doc) return jsonify({"hours": hours_list})
def checkEmail(): """ Takes in a json of the form {'email' : email_address} Returns a json of the form {'message' : error_message, 'valid' : Boolean} 'message' will refer to the specific reason an email address is invalid """ if request.json is None or 'email' not in request.json: abort(400) # Use the verification library to check that it is a valid email address = mailsane.normalize(request.json['email']) if address.error: return jsonify({'message': str(address), 'valid': False}) if dbworker.getUser(str(address)) is None: return jsonify({'message': 'Email address not found', 'valid': False}) return jsonify({'message': None, 'valid': True})
def authenticate(): # Use this route to log in and get a token # Takes in a json of the form {email : '', password : ''} if request.json is None: abort(400) for x in ['email', 'password']: if x not in request.json: abort(400) email = mailsane.normalize(request.json['email']) if email.error: abort(400) if dbworker.validateCredentials(str(email), request.json['password']): userType = dbworker.getUserType(str(email)) session['email'] = str(email) return jsonify({'userType': userType, 'success': True}) abort(401)
def getUser(): """ Takes in a JSON of {'email'} Returns {'result' : {user information, no id or password}, 'success' : True} This method is not just usable by admins, but by instructors """ if dbworker.validateAccessList( [dbworker.userTypeMap['admin'], dbworker.userTypeMap['instructor']]): pass else: abort(403) if request.json is None or 'email' not in request.json: abort(400) email = mailsane.normalize(request.json['email']) if email.error: abort(400) u = dbworker.getUser(str(email)) if u is None: abort(405) u.pop('password') u.pop('_id') now = datetime.datetime.now() bday = now if 'birthday' in u: bday = u['birthday'] delta = now - bday age = int(delta.total_seconds() / (31536000)) u['age'] = age return jsonify({'result': u, 'success': True})
def getStudentList(self, index): """ Construct a list of students. Check to make sure each column and cell for the table is filled with valid data for a new student. All students that are already in the database only need their email. :param index: the sheet index in the table dictionary :return: list of student emails """ global studentAttributes studentList = [] self.failures['Students'][index] = [] # For each row in the dict for user in self.tableDict['Students'][index]: fail = False newUser = True thisStudentInfo = [] email = '' # Check if there is a username column if 'MCC Account' in self.tableDict['Students'][index][user] and \ not pd.isnull(self.tableDict['Students'][index][user]['MCC Account']): email = self.tableDict['Students'][index][user]['MCC Account'] checkEmail = mailsane.normalize(email) if checkEmail.error: self.failures['Students'][index].append('Error at email for student at row ' + str(user+1)) fail = True # Check if Account already exists elif dbworker.mclient[dbworker.database]['users'].find_one({'userType': dbworker.userTypeMap['student'], 'email': email}) is not None: newUser = False studentList.append(email) # If account doesn't exist, extract the student info from the table else: attributeIndex = 1 while attributeIndex < len(studentAttributes) and not fail: if studentAttributes[attributeIndex] in self.tableDict['Students'][index][user] and \ not pd.isnull(self.tableDict['Students'][index][user][studentAttributes[attributeIndex]]): thisStudentInfo.append(self.tableDict['Students'][index][user][studentAttributes[attributeIndex]]) else: self.failures['Students'][index].append('Error at ' + studentAttributes[attributeIndex] + ' for student at row ' + str(user+1)) fail = True attributeIndex += 1 else: self.failures['Students'][index].append('Error at MCC Account for student at row ' + str(user+1)) fail = True # If all the parameters are met, add user info to the database if (not fail) and newUser: dbworker.createUser(email, thisStudentInfo[0], thisStudentInfo[1], thisStudentInfo[2], thisStudentInfo[3], 4, thisStudentInfo[4], thisStudentInfo[5], thisStudentInfo[6]) studentList.append(email) return studentList
def editUser(): """ Takes in a json of the form {'currentEmail' : email, 'newAttributes' : {...}} It can change any attribute that is not the email """ sys.stderr.write(str(request.json) + '\n') if not dbworker.validateAccess(dbworker.userTypeMap['admin']): abort(403) if request.json is None or 'currentEmail' not in request.json or 'newAttributes' not in request.json: abort(400) email = mailsane.normalize(request.json['currentEmail']) if email.error: abort(400) if dbworker.getUser(str(email)) is None: abort(404) if request.json['newAttributes'] == {} or 'email' in request.json[ 'newAttributes'] or '_id' in request.json['newAttributes']: # No changes requested or an attempt was made to change the email or _id abort(400) # Validate that all the changes made are valid # ie. ban changes to any invalid attributes try: validate(instance=request.json, schema=SchemaFactory.edit_user) except exceptions.ValidationError: abort(400) if 'birthday' in request.json[ 'newAttributes'] or 'password' in request.json['newAttributes']: # Convert birthday from string to datetime object # See https://stackoverflow.com/questions/969285/how-do-i-translate-an-iso-8601-datetime-string-into-a-python-datetime-object correctedTime = None try: if 'birthday' in request.json['newAttributes']: correctedTime = datetime.datetime.strptime( request.json['newAttributes']['birthday'], "%Y-%m-%dT%H:%M:%S.%fZ") except: abort(400) correctedDict = {} for x in request.json['newAttributes']: if x == 'birthday': correctedDict['birthday'] = correctedTime elif x == 'password': dbworker.setPassword(str(email), request.json['newAttributes']['password']) else: correctedDict[x] = request.json['newAttributes'][x] dbworker.editUser(str(email), correctedDict) else: dbworker.editUser(str(email), request.json['newAttributes']) return jsonify({'success': True})
def getReport(): """ Return a PDF containing all worked/volunteer hours """ report_params = request.json if report_params is None: abort(400) if 'email' not in report_params: report_params['email'] = session['email'] try: validate(instance=report_params, schema=SchemaFactory.report_hours) except exceptions.ValidationError: abort(400) email = mailsane.normalize(report_params['email']) if email.error: abort(400) if not dbworker.validateAccessList([dbworker.userTypeMap['admin'] ]) and str(email) != session['email']: # Allows admins to see everyones reports, users to see their own abort(403) paid_hrs = None filt = {"email": str(email)} proj = {'_id': 0, 'hours': 1} if 'paid' in request.json: filt['paid'] = True if request.json['paid'] else False paid_hrs = False if request.json['paid'] == 0 else True # Convert date ranges into datetime objects and insert into filter # Note: to enforce a specific date/time pattern you can also use strptime method: # datetime.datetime.strptime(request.json['startRange'], '%Y-%m-%d') (complete pattern: "%Y-%m-%dT%H:%M:%S.%fZ") if 'startRange' in report_params and 'endRange' in report_params: start_time_stamp = parse(report_params['startRange']) end_time_stamp = parse(report_params['endRange']) filt["dateTime"] = {'$gte': start_time_stamp, '$lte': end_time_stamp} elif 'startRange' in report_params: start_time_stamp = parse(report_params['startRange']) filt["dateTime"] = {'$gte': start_time_stamp} elif 'endRange' in report_params: end_time_stamp = parse(report_params['endRange']) filt["dateTime"] = {'$lte': end_time_stamp} hours = dbworker.getHours(filt=filt, projection=proj) hours_list = [] for doc in hours: hours_list.append(float(doc["hours"])) file_name = reportgen.hours(email, hours_list, paid_hrs) # Once generated, report PDFs are currently stored in the 'app' folder of docker container resp_file = send_file(file_name, attachment_filename=file_name) if os.path.exists("app/" + file_name): os.remove("app/" + file_name) return resp_file abort(500)