def log_coach_report_view(request): """Record coach report view by teacher""" if "facility_user" in request.session: try: # Log a "begin" and end here user = request.session["facility_user"] UserLog.begin_user_activity(user, activity_type="coachreport") UserLog.update_user_activity(user, activity_type="login") # to track active login time for teachers UserLog.end_user_activity(user, activity_type="coachreport") except ValidationError as e: # Never report this error; don't want this logging to block other functionality. logging.error("Failed to update Teacher userlog activity login: %s" % e)
def account_management(request): # Only log 'coachreport' activity for students, # (otherwise it's hard to compare teachers) if "facility_user" in request.session and not request.session["facility_user"].is_teacher and reverse("login") not in request.META.get("HTTP_REFERER", ""): try: # Log a "begin" and end here user = request.session["facility_user"] UserLog.begin_user_activity(user, activity_type="coachreport") UserLog.update_user_activity(user, activity_type="login") # to track active login time for teachers UserLog.end_user_activity(user, activity_type="coachreport") except ValidationError as e: # Never report this error; don't want this logging to block other functionality. logging.error("Failed to update student userlog activity: %s" % e) return student_view_context(request)
def account_management(request): # Only log 'coachreport' activity for students, # (otherwise it's hard to compare teachers) if "facility_user" in request.session and not request.session["facility_user"].is_teacher: try: # Log a "begin" and end here user = request.session["facility_user"] UserLog.begin_user_activity(user, activity_type="coachreport") UserLog.update_user_activity(user, activity_type="login") # to track active login time for teachers UserLog.end_user_activity(user, activity_type="coachreport") except ValidationError as e: # Never report this error; don't want this logging to block other functionality. logging.error("Failed to update student userlog activity: %s" % e) c = student_view_context(request) c["restricted"] = settings.DISABLE_SELF_ADMIN return c
def account_management(request): # Only log 'coachreport' activity for students, # (otherwise it's hard to compare teachers) if "facility_user" in request.session and not request.session["facility_user"].is_teacher: try: # Log a "begin" and end here user = request.session["facility_user"] UserLog.begin_user_activity(user, activity_type="coachreport") UserLog.update_user_activity(user, activity_type="login") # to track active login time for teachers UserLog.end_user_activity(user, activity_type="coachreport") except ValidationError as e: # Never report this error; don't want this logging to block other functionality. logging.error("Failed to update student userlog activity: %s" % e) c = student_view_context(request) c['restricted'] = settings.DISABLE_SELF_ADMIN return c
def tabular_view(request, facility, report_type="exercise"): """Tabular view also gets data server-side.""" # Define how students are ordered--used to be as efficient as possible. student_ordering = ["last_name", "first_name", "username"] # Get a list of topics (sorted) and groups topics = [get_node_cache("Topic").get(tid) for tid in get_knowledgemap_topics()] (groups, facilities) = get_accessible_objects_from_logged_in_user(request, facility=facility) context = plotting_metadata_context(request, facility=facility) context.update( { # For translators: the following two translations are nouns "report_types": (_("exercise"), _("video")), "request_report_type": report_type, "topics": [{"id": t[0]["id"], "title": t[0]["title"]} for t in topics if t], } ) # get querystring info topic_id = request.GET.get("topic", "") # No valid data; just show generic if not topic_id or not re.match("^[\w\-]+$", topic_id): return context group_id = request.GET.get("group", "") if group_id: # Narrow by group users = FacilityUser.objects.filter(group=group_id, is_teacher=False).order_by(*student_ordering) elif facility: # Narrow by facility search_groups = [groups_dict["groups"] for groups_dict in groups if groups_dict["facility"] == facility.id] assert len(search_groups) <= 1, "Should only have one or zero matches." # Return groups and ungrouped search_groups = search_groups[0] # make sure to include ungrouped students users = FacilityUser.objects.filter( Q(group__in=search_groups) | Q(group=None, facility=facility), is_teacher=False ).order_by(*student_ordering) else: # Show all (including ungrouped) for groups_dict in groups: search_groups += groups_dict["groups"] users = FacilityUser.objects.filter(Q(group__in=search_groups) | Q(group=None), is_teacher=False).order_by( *student_ordering ) # We have enough data to render over a group of students # Get type-specific information if report_type == "exercise": # Fill in exercises exercises = get_topic_exercises(topic_id=topic_id) exercises = sorted(exercises, key=lambda e: (e["h_position"], e["v_position"])) context["exercises"] = exercises # More code, but much faster exercise_names = [ex["name"] for ex in context["exercises"]] # Get students context["students"] = [] exlogs = ( ExerciseLog.objects.filter(user__in=users, exercise_id__in=exercise_names) .order_by(*["user__%s" % field for field in student_ordering]) .values("user__id", "struggling", "complete", "exercise_id") ) exlogs = list(exlogs) # force the query to be evaluated exlog_idx = 0 for user in users: log_table = {} while exlog_idx < len(exlogs) and exlogs[exlog_idx]["user__id"] == user.id: log_table[exlogs[exlog_idx]["exercise_id"]] = exlogs[exlog_idx] exlog_idx += 1 context["students"].append( { # this could be DRYer "first_name": user.first_name, "last_name": user.last_name, "username": user.username, "name": user.get_name(), "id": user.id, "exercise_logs": log_table, } ) elif report_type == "video": # Fill in videos context["videos"] = get_topic_videos(topic_id=topic_id) # More code, but much faster video_ids = [vid["id"] for vid in context["videos"]] # Get students context["students"] = [] vidlogs = ( VideoLog.objects.filter(user__in=users, video_id__in=video_ids) .order_by(*["user__%s" % field for field in student_ordering]) .values("user__id", "complete", "video_id", "total_seconds_watched", "points") ) vidlogs = list(vidlogs) # force the query to be executed now vidlog_idx = 0 for user in users: log_table = {} while vidlog_idx < len(vidlogs) and vidlogs[vidlog_idx]["user__id"] == user.id: log_table[vidlogs[vidlog_idx]["video_id"]] = vidlogs[vidlog_idx] vidlog_idx += 1 context["students"].append( { # this could be DRYer "first_name": user.first_name, "last_name": user.last_name, "username": user.username, "name": user.get_name(), "id": user.id, "video_logs": log_table, } ) else: raise Http404(_("Unknown report_type: %(report_type)s") % {"report_type": report_type}) if "facility_user" in request.session: try: # Log a "begin" and end here user = request.session["facility_user"] UserLog.begin_user_activity(user, activity_type="coachreport") UserLog.update_user_activity(user, activity_type="login") # to track active login time for teachers UserLog.end_user_activity(user, activity_type="coachreport") except ValidationError as e: # Never report this error; don't want this logging to block other functionality. logging.error("Failed to update Teacher userlog activity login: %s" % e) return context
def login(self, request, **kwargs): self.method_check(request, allowed=['post']) logout(request) data = self.deserialize(request, request.body, format=request.META.get( 'CONTENT_TYPE', 'application/json')) username = data.get('username', '') password = data.get('password', '') facility = data.get('facility', '') # first try logging in as a Django user if not settings.CENTRAL_SERVER: user = authenticate(username=username, password=password) if user: login(request, user) return self.create_response( request, { 'success': True, 'redirect': reverse("zone_redirect") }) # Find all matching users users = FacilityUser.objects.filter(username=username, facility=facility) if users.count() == 0: if Facility.objects.count() > 1: error_message = _( "Username and password do not match. Make sure you choose the right facility." ) else: error_message = _("Username and password do not match.") return self.create_response( request, { 'messages': { 'error': error_message }, 'error_highlight': "password" }, HttpUnauthorized) for user in users: if settings.SIMPLIFIED_LOGIN and not user.is_teacher: # For simplified login, as long as it is a student account just take the first one! break # if we find a user whose password matches, stop looking if user.check_password(password): break else: user = None if not user: if Facility.objects.count() > 1: error_message = _( "Username and password do not match. Make sure you choose the right facility." ) else: error_message = _("Username and password do not match.") return self.create_response( request, { 'messages': { 'error': error_message }, 'error_highlight': "password" }, HttpUnauthorized) else: try: UserLog.begin_user_activity( user, activity_type="login", language=lcode_to_django_lang(request.language) ) # Success! Log the event (ignoring validation failures) except ValidationError as e: logging.error("Failed to begin_user_activity upon login: %s" % e) request.session["facility_user"] = user messages.success( request, _("You've been logged in! We hope you enjoy your time with KA Lite " ) + _("-- be sure to log out when you finish.")) extras = {'success': True} if user.is_teacher: extras.update({ "redirect": reverse("coach_reports", kwargs={ "zone_id": getattr(Device.get_own_device().get_zone(), "id", "None") }) }) return self.create_response(request, extras)
def api_data(request, xaxis="", yaxis=""): """Request contains information about what data are requested (who, what, and how). Response should be a JSON object * data contains the data, structred by user and then datatype * the rest of the data is metadata, useful for displaying detailed info about data. """ # Get the request form try: form = get_data_form(request, xaxis=xaxis, yaxis=yaxis) # (data=request.REQUEST) except Exception as e: # In investigating #1509: we can catch SQL errors here and communicate clearer error # messages with the user here. For now, we have no such error to catch, so just # pass the errors on to the user (via the @api_handle_error_with_json decorator). raise e # Query out the data: who? if form.data.get("user"): facility = [] groups = [] users = [get_object_or_404(FacilityUser, id=form.data.get("user"))] elif form.data.get("group"): facility = [] groups = [get_object_or_404(FacilityGroup, id=form.data.get("group"))] users = FacilityUser.objects.filter(group=form.data.get("group"), is_teacher=False).order_by("last_name", "first_name") elif form.data.get("facility"): facility = get_object_or_404(Facility, id=form.data.get("facility")) groups = FacilityGroup.objects.filter(facility__in=[form.data.get("facility")]) users = FacilityUser.objects.filter(facility__in=[form.data.get("facility")], is_teacher=False).order_by("last_name", "first_name") else: return HttpResponseNotFound(_("Did not specify facility, group, nor user.")) # Query out the data: where? if not form.data.get("topic_path"): return HttpResponseNotFound(_("Must specify a topic path")) # Query out the data: what? computed_data = compute_data(data_types=[form.data.get("xaxis"), form.data.get("yaxis")], who=users, where=form.data.get("topic_path")) # Quickly add back in exercise meta-data (could potentially be used in future for other data too!) ex_nodes = get_node_cache()["Exercise"] exercises = [] for e in computed_data["exercises"]: exercises.append({ "slug": e, "full_name": ex_nodes[e][0]["display_name"], "url": ex_nodes[e][0]["path"], }) json_data = { "data": computed_data["data"], "exercises": exercises, "videos": computed_data["videos"], "users": dict(zip([u.id for u in users], ["%s, %s" % (u.last_name, u.first_name) for u in users] )), "groups": dict(zip([g.id for g in groups], dict(zip(["id", "name"], [(g.id, g.name) for g in groups])), )), "facility": None if not facility else { "name": facility.name, "id": facility.id, } } if "facility_user" in request.session: try: # Log a "begin" and end here user = request.session["facility_user"] UserLog.begin_user_activity(user, activity_type="coachreport") UserLog.update_user_activity(user, activity_type="login") # to track active login time for teachers UserLog.end_user_activity(user, activity_type="coachreport") except ValidationError as e: # Never report this error; don't want this logging to block other functionality. logging.error("Failed to update Teacher userlog activity login: %s" % e) # Now we have data, stream it back with a handler for date-times return JsonResponse(json_data)
def tabular_view(request, facility, report_type="exercise"): """Tabular view also gets data server-side.""" # Define how students are ordered--used to be as efficient as possible. student_ordering = ["last_name", "first_name", "username"] # Get a list of topics (sorted) and groups topics = [ get_node_cache("Topic").get(tid) for tid in get_knowledgemap_topics() ] (groups, facilities) = get_accessible_objects_from_logged_in_user( request, facility=facility) context = plotting_metadata_context(request, facility=facility) context.update({ # For translators: the following two translations are nouns "report_types": (_("exercise"), _("video")), "request_report_type": report_type, "topics": [{ "id": t[0]["id"], "title": t[0]["title"] } for t in topics if t], }) # get querystring info topic_id = request.GET.get("topic", "") # No valid data; just show generic if not topic_id or not re.match("^[\w\-]+$", topic_id): return context group_id = request.GET.get("group", "") if group_id: # Narrow by group users = FacilityUser.objects.filter( group=group_id, is_teacher=False).order_by(*student_ordering) elif facility: # Narrow by facility search_groups = [ groups_dict["groups"] for groups_dict in groups if groups_dict["facility"] == facility.id ] assert len(search_groups) <= 1, "Should only have one or zero matches." # Return groups and ungrouped search_groups = search_groups[ 0] # make sure to include ungrouped students users = FacilityUser.objects.filter( Q(group__in=search_groups) | Q(group=None, facility=facility), is_teacher=False).order_by(*student_ordering) else: # Show all (including ungrouped) for groups_dict in groups: search_groups += groups_dict["groups"] users = FacilityUser.objects.filter( Q(group__in=search_groups) | Q(group=None), is_teacher=False).order_by(*student_ordering) # We have enough data to render over a group of students # Get type-specific information if report_type == "exercise": # Fill in exercises exercises = get_topic_exercises(topic_id=topic_id) exercises = sorted(exercises, key=lambda e: (e["h_position"], e["v_position"])) context["exercises"] = exercises # More code, but much faster exercise_names = [ex["name"] for ex in context["exercises"]] # Get students context["students"] = [] exlogs = ExerciseLog.objects \ .filter(user__in=users, exercise_id__in=exercise_names) \ .order_by(*["user__%s" % field for field in student_ordering]) \ .values("user__id", "struggling", "complete", "exercise_id") exlogs = list(exlogs) # force the query to be evaluated exlog_idx = 0 for user in users: log_table = {} while exlog_idx < len( exlogs) and exlogs[exlog_idx]["user__id"] == user.id: log_table[exlogs[exlog_idx]["exercise_id"]] = exlogs[exlog_idx] exlog_idx += 1 context["students"].append({ # this could be DRYer "first_name": user.first_name, "last_name": user.last_name, "username": user.username, "name": user.get_name(), "id": user.id, "exercise_logs": log_table, }) elif report_type == "video": # Fill in videos context["videos"] = get_topic_videos(topic_id=topic_id) # More code, but much faster video_ids = [vid["id"] for vid in context["videos"]] # Get students context["students"] = [] vidlogs = VideoLog.objects \ .filter(user__in=users, video_id__in=video_ids) \ .order_by(*["user__%s" % field for field in student_ordering])\ .values("user__id", "complete", "video_id", "total_seconds_watched", "points") vidlogs = list(vidlogs) # force the query to be executed now vidlog_idx = 0 for user in users: log_table = {} while vidlog_idx < len( vidlogs) and vidlogs[vidlog_idx]["user__id"] == user.id: log_table[vidlogs[vidlog_idx] ["video_id"]] = vidlogs[vidlog_idx] vidlog_idx += 1 context["students"].append({ # this could be DRYer "first_name": user.first_name, "last_name": user.last_name, "username": user.username, "name": user.get_name(), "id": user.id, "video_logs": log_table, }) else: raise Http404( _("Unknown report_type: %(report_type)s") % {"report_type": report_type}) if "facility_user" in request.session: try: # Log a "begin" and end here user = request.session["facility_user"] UserLog.begin_user_activity(user, activity_type="coachreport") UserLog.update_user_activity( user, activity_type="login" ) # to track active login time for teachers UserLog.end_user_activity(user, activity_type="coachreport") except ValidationError as e: # Never report this error; don't want this logging to block other functionality. logging.error( "Failed to update Teacher userlog activity login: %s" % e) return context
def login(request, facility): facility_id = (facility and facility.id) or None facilities = list(Facility.objects.all()) #Fix for #2047: prompt user to create an admin account if none exists if not User.objects.exists(): messages.warning(request, _("No administrator account detected. Please run 'python manage.py createsuperuser' from the terminal to create one.")) # Fix for #1211: refresh cached facility info when it's free and relevant refresh_session_facility_info(request, facility_count=len(facilities)) if request.method != 'POST': # render the unbound login form referer = urlparse.urlparse(request.META["HTTP_REFERER"]).path if request.META.get("HTTP_REFERER") else None # never use the homepage as the referer if referer in [reverse("homepage"), reverse("add_facility_student")]: referer = None form = LoginForm(initial={"facility": facility_id, "callback_url": referer}) else: # process the login form # log out any Django user or facility user logout(request) username = request.POST.get("username", "") password = request.POST.get("password", "") # first try logging in as a Django user if not settings.CENTRAL_SERVER: user = authenticate(username=username, password=password) if user: auth_login(request, user) return HttpResponseRedirect(request.next or reverse("zone_redirect")) # try logging in as a facility user form = LoginForm(data=request.POST, request=request, initial={"facility": facility_id}) if not form.is_valid(): messages.error( request, _("There was an error logging you in. Please correct any errors listed below, and try again."), ) else: user = form.get_user() try: UserLog.begin_user_activity(user, activity_type="login", language=request.language) # Success! Log the event (ignoring validation failures) except ValidationError as e: logging.error("Failed to begin_user_activity upon login: %s" % e) request.session["facility_user"] = user messages.success(request, _("You've been logged in! We hope you enjoy your time with KA Lite ") + _("-- be sure to log out when you finish.")) # Send them back from whence they came landing_page = form.cleaned_data["callback_url"] if not landing_page: # Just going back to the homepage? We can do better than that. landing_page = reverse("coach_reports") if form.get_user().is_teacher else None landing_page = landing_page or (reverse("account_management") if False else reverse("homepage")) # TODO: pass the redirect as a parameter. return HttpResponseRedirect(form.non_field_errors() or request.next or landing_page) return { "form": form, "facilities": facilities, "sign_up_url": reverse("add_facility_student"), }
def login(self, request, **kwargs): self.method_check(request, allowed=['post']) logout(request) data = self.deserialize(request, request.body, format=request.META.get('CONTENT_TYPE', 'application/json')) username = data.get('username', '') password = data.get('password', '') facility = data.get('facility', '') # first try logging in as a Django user if not settings.CENTRAL_SERVER: user = authenticate(username=username, password=password) if user: login(request, user) return self.create_response(request, { 'success': True, 'redirect': reverse("zone_redirect") }) # Find all matching users users = FacilityUser.objects.filter(username=username, facility=facility) if users.count() == 0: if Facility.objects.count() > 1: error_message = _("Username and password do not match. Make sure you choose the right facility.") else: error_message = _("Username and password do not match.") return self.create_response(request, { 'messages': {'error': error_message}, 'error_highlight': "password" }, HttpUnauthorized ) for user in users: if settings.SIMPLIFIED_LOGIN and not user.is_teacher: # For simplified login, as long as it is a student account just take the first one! break # if we find a user whose password matches, stop looking if user.check_password(password): break else: user = None if not user: if Facility.objects.count() > 1: error_message = _("Username and password do not match. Make sure you choose the right facility.") else: error_message = _("Username and password do not match.") return self.create_response(request, { 'messages': {'error': error_message}, 'error_highlight': "password" }, HttpUnauthorized ) else: try: UserLog.begin_user_activity(user, activity_type="login", language=lcode_to_django_lang(request.language)) # Success! Log the event (ignoring validation failures) except ValidationError as e: logging.error("Failed to begin_user_activity upon login: %s" % e) request.session["facility_user"] = user messages.success(request, _("You've been logged in! We hope you enjoy your time with KA Lite ") + _("-- be sure to log out when you finish.")) extras = {'success': True} if user.is_teacher: extras.update({ "redirect": reverse("coach_reports", kwargs={"zone_id": getattr(Device.get_own_device().get_zone(), "id", "None")}) }) return self.create_response(request, extras)
def login(request, facility): facility_id = (facility and facility.id) or None facilities = list(Facility.objects.all()) # Fix for #1211: refresh cached facility info when it's free and relevant refresh_session_facility_info(request, facility_count=len(facilities)) if request.method != 'POST': # render the unbound login form referer = urlparse.urlparse( request.META["HTTP_REFERER"]).path if request.META.get( "HTTP_REFERER") else None # never use the homepage as the referer if referer in [reverse("homepage"), reverse("add_facility_student")]: referer = None form = LoginForm(initial={ "facility": facility_id, "callback_url": referer }) else: # process the login form # log out any Django user or facility user logout(request) username = request.POST.get("username", "") password = request.POST.get("password", "") # first try logging in as a Django user if not settings.CENTRAL_SERVER: user = authenticate(username=username, password=password) if user: auth_login(request, user) return HttpResponseRedirect(request.next or reverse("zone_redirect")) # try logging in as a facility user form = LoginForm(data=request.POST, request=request, initial={"facility": facility_id}) if not form.is_valid(): messages.error( request, _("There was an error logging you in. Please correct any errors listed below, and try again." ), ) else: user = form.get_user() try: UserLog.begin_user_activity( user, activity_type="login", language=request.language ) # Success! Log the event (ignoring validation failures) except ValidationError as e: logging.error("Failed to begin_user_activity upon login: %s" % e) request.session["facility_user"] = user messages.success( request, _("You've been logged in! We hope you enjoy your time with KA Lite " ) + _("-- be sure to log out when you finish.")) # Send them back from whence they came landing_page = form.cleaned_data["callback_url"] if not landing_page: # Just going back to the homepage? We can do better than that. landing_page = reverse( "coach_reports") if form.get_user().is_teacher else None landing_page = landing_page or ( reverse("account_management") if False else reverse("homepage") ) # TODO: pass the redirect as a parameter. return HttpResponseRedirect(form.non_field_errors() or request.next or landing_page) return { "form": form, "facilities": facilities, "sign_up_url": reverse("add_facility_student"), }