def get_lesson_json(self, lesson_code): from model import Lesson import json import datetime def handler(o): if isinstance(o, datetime.datetime): return "(new Date(%d, %d, %d, %d, %d, %d))"%( o.year, o.month, o.day, o.hour, o.minute, o.second) else: raise TypeError(repr(o)) lesson = Lesson.get_by_key_name(lesson_code) lesson_info = [] if not lesson.is_deleted: lesson_info.append({ "lesson_code" : lesson.lesson_code, "title" : lesson.title, "description" : lesson.description, "class_name" : lesson.class_name, "start_time" : lesson.start_time, "stop_time" : lesson.stop_time, "tasks" : lesson.tasks, "is_active" : lesson.is_active }) lesson_json = json.dumps(lesson_info, default=handler) return lesson_json
def make_lesson_code(self): import random from model import Lesson digits = 5 # This is essentially a do loop, but I'm using a generous upper bound to prevent the # possibility of an endless (and potentially costly) spin, in case of a bug, for example. for i in range(1000): assert i < 1000 - 1, "Looks like infinite loop." n = random.randint(0,10**digits - 1) lesson_code = "%05d"%n lesson = Lesson.get_by_key_name(lesson_code) if lesson is None: break return lesson_code
def get(self): self.load_search_party_context(user_type="teacher") try: if not self.is_teacher: raise NotAnAuthenticatedTeacherError() else: lesson = None form_item = lambda key:self.request.get(key, "").strip() if form_item is not None: lesson_code = form_item("lesson_code") if lesson_code != "": from model import Lesson lesson = Lesson.get_by_key_name(lesson_code) action = form_item("action") if action=="create": self.create_edit_lesson(form_item) elif action=="clone": self.clone_lesson(lesson) elif action=="edit": self.create_edit_lesson(form_item) elif action=="start": self.start_lesson(lesson) elif action=="stop": self.stop_lesson(lesson) elif action=="stopall": self.stop_all_lessons() elif action=="clear": self.clear_lesson(lesson) elif action=="delete": self.delete_lesson(lesson) elif action=="deleteall": self.delete_all_lessons() elif action=="logoutstudent": from model import Student student_nickname = form_item("student_nickname") student_key = "::".join((student_nickname, lesson_code)) student = Student.get_by_key_name(student_key) self.log_out_student(student) elif action=="logoutallstudents": self.log_out_all_students(lesson) else: self.show_dashboard() except NotAnAuthenticatedTeacherError: self.redirect_to_teacher_login()
def post(self): from helpers import log self.load_user() self.person.add_client_id(self.client_id) if self.person_type=="student" and len(self.person.client_ids)==1: self.person.latest_logout_timestamp = None self.person.put() log("=> CHANNEL CONNECTED: {0} ({1})".format(str(self.client_id),len(self.person.client_ids))) if self.person_type=="student" and len(self.person.client_ids)==1: from client_id_utils import lesson_code_for_client_id from model import Lesson from updates import send_update_log_in lesson_code = lesson_code_for_client_id(self.client_id) lesson = Lesson.get_by_key_name(lesson_code) send_update_log_in(student=self.person, teacher=lesson.teacher)
def get(self, lesson_code): self.load_search_party_context(user_type="teacher") try: from model import Lesson import json import settings if not self.is_teacher: raise NotAnAuthenticatedTeacherError() lesson = Lesson.get_by_key_name(lesson_code) if lesson is None: raise LessonNotFoundError() if lesson.teacher_key != self.teacher_key: raise WrongTeacherError() teacher = self.person person_key = teacher.user.user_id(); token = self.create_channel(person_key=person_key, lesson_code=lesson_code) default_start_pane = "students" template_values = { 'header' : self.gen_header("teacher"), 'token' : token, 'lesson' : lesson, 'lesson_json' : self.get_lesson_json(lesson_code), 'students_js' : self.make_student_structure_js2(lesson=lesson, indent=" "), 'default_start_pane' : default_start_pane, 'debug_mode' : json.dumps(settings.DEBUG) } if self.session.has_key('msg'): template_values['msg'] = self.session.pop('msg') # only show the message once self.write_response_with_template("teacher.html", template_values) except NotAnAuthenticatedTeacherError: self.redirect_to_teacher_login() except LessonNotFoundError: self.redirect_with_msg("There was an internal error. Please choose your lesson to continue.", "/teacher_dashboard") except WrongTeacherError: self.redirect_to_teacher_login()
def create_edit_lesson(self, form_item): lesson_code = form_item("lesson_code") is_new = lesson_code == "" if is_new: lesson_code = self.make_lesson_code() lesson_title = form_item("lesson_title") lesson_description = form_item("lesson_description") class_name = form_item("class_name") task_infos = [] for task_num in range(1, int(self.request.get("max_num_tasks", "10"))+1): task_title = form_item("task_title_%d"%task_num) task_description = form_item("task_description_%d"%task_num) if task_title != "": task_infos.append((task_title, task_description)) import json tasks_json = json.dumps(task_infos) if (len(lesson_title) > 0) and (len(lesson_code) > 0) and (len(task_infos) > 0): from model import Lesson from datetime import datetime now = datetime.now() lesson = Lesson(key_name=lesson_code, teacher=self.person, title=lesson_title, lesson_code=lesson_code, description=lesson_description, class_name=class_name, tasks_json=tasks_json, start_time=now, stop_time=None) if not is_new: old_lesson = Lesson.get_by_key_name(lesson_code) lesson.start_time = old_lesson.start_time lesson.stop_time = old_lesson.stop_time lesson.put() self.response.out.write(self.get_lessons_json()) else: data = { 'error': 1, 'msg': 'Required fields are missing.' } self.response.out.write(json.dumps(data))
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 _send_tab_delimited_report(self, lesson_code, utc_offset): import StringIO from model import Student, StudentActivity, Lesson import helpers encoding = "UTF-8" lesson = Lesson.get_by_key_name(lesson_code) assert lesson is not None if lesson is None or lesson.teacher_key != self.teacher_key: self.write_response_plain_text("ERROR: Lesson code appears to be incorrect.") else: students = Student.fetch_all("lesson =", lesson) task_titles = tuple(task_info[0] for task_info in lesson.tasks) student_key_to_nickname = dict((s.key().name(), s.nickname) for s in students) activities = StudentActivity.fetch_all("lesson =", lesson) report_buffer = StringIO.StringIO() excel_writer = UnicodeWriter(report_buffer, "excel-tab", "utf8") headers = ( # "Lesson_Code", "Timestamp", "Student", "Task_Number", "Task_Name", "Activity_Type", "Query", "Link_URL", "Link_Title", "Is_Helpful", "Answer_Text", "Answer_Explanation" ) excel_writer.writerow(headers) for activity in activities: student_key = activity.student_key.name() student_nickname = student_key_to_nickname[student_key] timestamp = (activity.timestamp - utc_offset).strftime("%m/%d/%Y %H:%M:%S") task_idx = activity.task_idx task_title = task_titles[task_idx] task_num = task_idx + 1 line_parts = ( # lesson_code, timestamp, student_nickname, task_num, task_title, activity.activity_type, activity.search, activity.link, activity.link_title, activity.is_helpful, activity.answer_text, activity.answer_explanation ) # line_parts = tuple(unicode(p).encode("utf8") for p in line_parts) excel_writer.writerow(line_parts) report_text = report_buffer.getvalue() report_buffer.close() content_type = "text/tab-separated-values" filename = "search_party_lesson_%s_activity_as_of_%s.txt"%(lesson_code, helpers.timestamp()) self.write_response_as_file(encoded_content=report_text, content_type=content_type, filename=filename, encoding=encoding)