def post(self): import json from model import Student, Lesson from all_exceptions import StudentLoginException from datetime import datetime from helpers import log try: self.load_search_party_context(user_type="student") # Get CGI form fields. lesson_code = self.request.get('lesson_code') student_nickname = self.request.get('student_nickname') ext = int(self.request.get('ext', 0)) # Normalize whitespace in student name. # Replace any string of >=1 whitespace with a single space (equivalent to s/\s+/ /g). student_nickname = " ".join(student_nickname.split()) if not lesson_code: # No lesson code raise StudentLoginException("Please enter a lesson code.", "lesson_code==%r"%lesson_code) # If no student nickname, generate an anonymous one anonymous = False if not student_nickname: import random, string alphabet = string.letters + string.digits anonymous_student = None for i in range(8): random_nickname = "".join(random.choice(alphabet) for i in range(10)) key_name = Student.make_key_name(student_nickname=random_nickname, lesson_code=lesson_code) anonymous_student = Student.get_by_key_name(key_name) if anonymous_student is None: student_nickname = random_nickname anonymous = True break if anonymous and not student_nickname: # No student name raise StudentLoginException("Could not login as anonymous student.", "student_nickname==%r"%student_nickname) # if not lesson_code and not student_nickname: # # Blank form # raise StudentLoginException("Please enter a lesson code and a student name.", # "lesson_code==%r, student_nickname==%r"%(lesson_code, student_nickname)) # elif not lesson_code: # # No lesson code # raise StudentLoginException("Please enter a lesson code.", # "lesson_code==%r"%lesson_code) # elif not student_nickname: # # No student name # raise StudentLoginException("Please enter a student name.", # "student_nickname==%r"%student_nickname) lesson = Lesson.get_by_key_name(lesson_code) # Retrieve lesson from DB # - If lesson does not exist, this will return None. # - If lesson existed but is disabled, it will return the lesson, but lesson.is_active will be False. # - If lesson existed but was deleted (hidden), it will return the lesson, but lesson.is_deleted will be True. # (Deleting lessons is done lazily. Actually, they are merely hidden from the teacher's view.) if lesson is None or lesson.is_deleted: # Lesson does not exist or was deleted (hidden). raise StudentLoginException("Please check the lesson code.", "lesson retrieved from datastore with lesson_code %r is None"%lesson_code) elif not lesson.is_active: # Lesson has been disabled by teacher. Students are not allowed to work on it anymore. raise StudentLoginException("This lesson is finished. You cannot work on it now.", "lesson_code %r has is_active=False"%lesson_code) # Fetch student from DB. # - Might return None if nobody has ever logged in with this nickname+lesson combination. key_name = Student.make_key_name(student_nickname=student_nickname, lesson_code=lesson_code) student = Student.get_by_key_name(key_name) login_timestamp = datetime.now() if student is not None: # Found the student. student.session_sid=self.session.sid student.latest_login_timestamp = login_timestamp student.latest_logout_timestamp = None if not student.first_login_timestamp: student.first_login_timestamp = login_timestamp else: student = Student( key_name=key_name, nickname=student_nickname, teacher=lesson.teacher_key, lesson=lesson, task_idx=self.INITIAL_TASK_IDX, first_login_timestamp=login_timestamp, latest_login_timestamp=login_timestamp, latest_logout_timestamp=None, session_sid=self.session.sid, anonymous=anonymous, client_ids=[] ) assert student.session_sid is not None student.put() self.set_person(student) displayName = "Anonymous" if self.is_student and self.person.anonymous else self.person.nickname self.session['msg'] = "Student logged in: Hello " + displayName self.response.headers.add_header('Content-Type', 'application/json', charset='utf-8') self.response.out.write(json.dumps({"status":"logged_in", "ext":ext})) log( "=> LOGIN SUCCESS" ) except StudentLoginException, e: e.log() self.set_person(None) msg = e.args[0] self.session['msg'] = msg self.response.headers.add_header('Content-Type', 'application/json', charset='utf-8') self.response.out.write(json.dumps({"status":"logged_out", "error":msg})) log( "=> LOGIN FAILURE: %s"%msg )
def _attempt_to_identify_as_student(self): from model import Student from helpers import log # Initialize student # We still store changes to student record only, if any, but we don't want to store # the record needlessly, since that consumes billable resources. student = None student_is_dirty = False # There are two ways to identify a student: nickname + lesson code sent via CGI, # or the session ID. We will try them in that order. # Get CGI form values lesson_code = self.request.get("lesson_code", None) student_nickname = self.request.get("student_nickname", None) if lesson_code is not None and student_nickname is not None: # 1. Fetch student by nickname+lesson student_nickname = self.htmlunquote(student_nickname) key_name = Student.make_key_name(student_nickname=student_nickname, lesson_code=lesson_code) student = Student.get_by_key_name(key_name) log("=> SEARCHING BY NAME AND LESSON CODE: {0}, {1}, {2}".format(student_nickname, lesson_code, key_name)) if student is not None and self.session.sid != student.session_sid: if student.is_logged_in: # Prevent login if student already logged in to another session # GAE limits # of channels so we don't want to allow too many student windows from all_exceptions import StudentLoginException raise StudentLoginException( "Please choose another name. Someone is already logged in as %s." % (student_nickname.encode("ascii", "xmlcharrefreplace")), "Session ID doesn't match.", student.session_sid, self.session.sid, student.latest_login_timestamp, student.latest_logout_timestamp, ) # else: # # Need to update session id if student was logged into another browser previously (and then logged out) # student.session_sid = self.session.sid # student_is_dirty = True else: # 2. Fetch student by session id student = Student.all().filter("session_sid =", self.session.sid).get() log("=> SEARCHING FOR STUDENT BY SESSION ID {0}".format(self.session.sid)) if student is not None and not student.is_logged_in: # Passively log student in again. # At some point this student logged into this browser but logged out passively. from datetime import datetime student.latest_login_timestamp = datetime.now() student.latest_logout_timestamp = None student_is_dirty = True log("=> PASSIVE STUDENT LOGIN AFTER PASSIVE LOGOUT") if student_is_dirty: # Store changes to student record, if any. student.put() if student is not None and not student.is_logged_in: log("=> STUDENT IS **NOT** LOGGED IN") return student