Ejemplo n.º 1
0
    def get(self):
        user_data = UserData.current()

        if user_data:

            user_data_override = self.request_user_data("coach_email")
            if user_util.is_current_user_developer() and user_data_override:
                user_data = user_data_override

            invalid_student = self.request_bool("invalid_student",
                                                default=False)

            coach_requests = [
                x.student_requested_data.email
                for x in CoachRequest.get_for_coach(user_data)
                if x.student_requested_data
            ]

            student_lists_models = StudentList.get_for_coach(user_data.key())
            student_lists_list = []
            for student_list in student_lists_models:
                student_lists_list.append({
                    'key': str(student_list.key()),
                    'name': student_list.name,
                })
            student_lists_dict = dict(
                (g['key'], g) for g in student_lists_list)

            students_data = user_data.get_students_data()
            students = map(
                lambda s: {
                    'key':
                    str(s.key()),
                    'email':
                    s.email,
                    'nickname':
                    s.nickname,
                    'student_lists': [
                        l for l in [
                            student_lists_dict.get(str(list_id))
                            for list_id in s.student_lists
                        ] if l
                    ],
                }, students_data)
            students.sort(key=lambda s: s['nickname'])

            template_values = {
                "students": students,
                "students_json": json.dumps(students),
                "student_lists": student_lists_list,
                "student_lists_json": json.dumps(student_lists_list),
                "invalid_student": invalid_student,
                "coach_requests": coach_requests,
                "coach_requests_json": json.dumps(coach_requests),
                'selected_nav_link': 'coach'
            }
            self.render_jinja2_template('viewstudentlists.html',
                                        template_values)
        else:
            self.redirect(util.create_login_url(self.request.uri))
Ejemplo n.º 2
0
    def get(self):
        user_data = UserData.current()
        user_data_override = self.request_user_data("coach_email")
        if user_util.is_current_user_developer() and user_data_override:
            user_data = user_data_override

        invalid_student = self.request_bool("invalid_student", default=False)

        coach_requests = [req.student_requested_identifier
                          for req in CoachRequest.get_for_coach(user_data)
                          if req.student_requested_data]

        student_lists_models = StudentList.get_for_coach(user_data.key())
        student_lists_list = []
        for student_list in student_lists_models:
            student_lists_list.append({
                'key': str(student_list.key()),
                'name': student_list.name,
            })
        student_lists_dict = dict((g['key'], g) for g in student_lists_list)

        def student_to_dict(s):
            """Convert the UserData s to a dictionary for rendering."""
            return {
                'key': str(s.key()),
                'email': s.email,
                'username': s.username,
                # Note that child users don't have an email.
                'identifier': s.username or s.email,
                'nickname': s.nickname,
                'profile_root': s.profile_root,
                'can_modify_coaches': s.can_modify_coaches(),
                'studentLists':
                        [l for l in [student_lists_dict.get(str(list_id))
                           for list_id in s.student_lists] if l],
            }

        students_data = user_data.get_students_data()
        students = map(student_to_dict, students_data)
        students.sort(key=lambda s: s['nickname'])

        child_accounts = map(student_to_dict, user_data.get_child_users())
        template_values = {
            'students': students,
            'child_accounts': child_accounts,
            'child_account_keyset': set([c['key'] for c in child_accounts]),
            'students_json': json.dumps(students),
            'student_lists': student_lists_list,
            'student_lists_json': json.dumps(student_lists_list),
            'invalid_student': invalid_student,
            'coach_requests': coach_requests,
            'coach_requests_json': json.dumps(coach_requests),
            'selected_nav_link': 'coach',
            'email': user_data.email,
        }
        self.render_jinja2_template('viewstudentlists.html', template_values)
Ejemplo n.º 3
0
    def get(self):
        if not user_util.is_current_user_developer():
            if App.dashboard_secret and self.request_string("x", default=None) != App.dashboard_secret:
                self.redirect(users.create_login_url(self.request.uri))
                return

        kinds = self.request_string("kinds", Dashboard.DEFAULT_KINDS).split(',')

        graphs = []
        for kind in kinds:
            d = daily_graph_context(EntityStatistic(kind), "data")
            d['kind_name'] = kind
            d['title'] = "%s created per day" % kind
            graphs.append(d)

        self.render_jinja2_template("dashboard/dashboard.html", {'graphs': graphs})
Ejemplo n.º 4
0
    def get(self):
        if not user_util.is_current_user_developer():
            if App.dashboard_secret and self.request_string("x", default=None) != App.dashboard_secret:
                self.redirect(users.create_login_url(self.request.uri))
                return

        kinds = self.request_string("kinds", Dashboard.DEFAULT_KINDS).split(',')

        graphs = []
        for kind in kinds:
            d = daily_graph_context(EntityStatistic(kind), "data")
            d['kind_name'] = kind
            d['title'] = "%s created per day" % kind
            graphs.append(d)

        self.render_jinja2_template("dashboard/dashboard.html", {'graphs': graphs})
Ejemplo n.º 5
0
    def get(self):
        user_data = UserData.current()

        if user_data:

            user_data_override = self.request_user_data("coach_email")
            if user_util.is_current_user_developer() and user_data_override:
                user_data = user_data_override

            invalid_student = self.request_bool("invalid_student", default = False)

            coach_requests = [x.student_requested_data.email for x in CoachRequest.get_for_coach(user_data) if x.student_requested_data]

            student_lists_models = StudentList.get_for_coach(user_data.key())
            student_lists_list = [];
            for student_list in student_lists_models:
                student_lists_list.append({
                    'key': str(student_list.key()),
                    'name': student_list.name,
                })
            student_lists_dict = dict((g['key'], g) for g in student_lists_list)

            students_data = user_data.get_students_data()
            students = map(lambda s: {
                'key': str(s.key()),
                'email': s.email,
                'nickname': s.nickname,
                'profile_root': s.profile_root,
                'studentLists': [l for l in [student_lists_dict.get(str(list_id)) for list_id in s.student_lists] if l],
            }, students_data)
            students.sort(key=lambda s: s['nickname'])

            template_values = {
                "students": students,
                "students_json": json.dumps(students),
                "student_lists": student_lists_list,
                "student_lists_json": json.dumps(student_lists_list),
                "invalid_student": invalid_student,
                "coach_requests": coach_requests,
                "coach_requests_json": json.dumps(coach_requests),
                'selected_nav_link': 'coach'
            }
            self.render_jinja2_template('viewstudentlists.html', template_values)
        else:
            self.redirect(util.create_login_url(self.request.uri))
Ejemplo n.º 6
0
def attempt_problem(user_data, user_exercise, problem_number, attempt_number,
    attempt_content, sha1, seed, completed, count_hints, time_taken,
    review_mode, exercise_non_summative, problem_type, ip_address):

    if user_exercise and user_exercise.belongs_to(user_data):
        dt_now = datetime.datetime.now()
        exercise = user_exercise.exercise_model

        old_graph = user_exercise.get_user_exercise_graph()

        user_exercise.last_done = dt_now
        user_exercise.seconds_per_fast_problem = exercise.seconds_per_fast_problem
        user_exercise.summative = exercise.summative

        user_data.record_activity(user_exercise.last_done)

        # If a non-admin tries to answer a problem out-of-order, just ignore it
        if problem_number != user_exercise.total_done + 1 and not user_util.is_current_user_developer():
            # Only admins can answer problems out of order.
            raise QuietException("Problem number out of order (%s vs %s) for user_id: %s submitting attempt content: %s with seed: %s" % (problem_number, user_exercise.total_done + 1, user_data.user_id, attempt_content, seed))

        if len(sha1) <= 0:
            raise Exception("Missing sha1 hash of problem content.")

        if len(seed) <= 0:
            raise Exception("Missing seed for problem content.")

        if len(attempt_content) > 500:
            raise Exception("Attempt content exceeded maximum length.")

        # Build up problem log for deferred put
        problem_log = models.ProblemLog(
                key_name=models.ProblemLog.key_for(user_data, user_exercise.exercise, problem_number),
                user=user_data.user,
                exercise=user_exercise.exercise,
                problem_number=problem_number,
                time_taken=time_taken,
                time_done=dt_now,
                count_hints=count_hints,
                hint_used=count_hints > 0,
                correct=completed and not count_hints and (attempt_number == 1),
                sha1=sha1,
                seed=seed,
                problem_type=problem_type,
                count_attempts=attempt_number,
                attempts=[attempt_content],
                ip_address=ip_address,
                review_mode=review_mode,
        )

        if exercise.summative:
            problem_log.exercise_non_summative = exercise_non_summative

        first_response = (attempt_number == 1 and count_hints == 0) or (count_hints == 1 and attempt_number == 0)

        if user_exercise.total_done > 0 and user_exercise.streak == 0 and first_response:
            bingo('hints_keep_going_after_wrong')

        just_earned_proficiency = False

        # Users can only attempt problems for themselves, so the experiment
        # bucket always corresponds to the one for this current user
        struggling_model = StrugglingExperiment.get_alternative_for_user(
                 user_data, current_user=True) or StrugglingExperiment.DEFAULT
        if completed:

            user_exercise.total_done += 1

            if problem_log.correct:

                proficient = user_data.is_proficient_at(user_exercise.exercise)
                explicitly_proficient = user_data.is_explicitly_proficient_at(user_exercise.exercise)
                suggested = user_data.is_suggested(user_exercise.exercise)
                problem_log.suggested = suggested

                problem_log.points_earned = points.ExercisePointCalculator(user_exercise, suggested, proficient)
                user_data.add_points(problem_log.points_earned)

                # Streak only increments if problem was solved correctly (on first attempt)
                user_exercise.total_correct += 1
                user_exercise.streak += 1
                user_exercise.longest_streak = max(user_exercise.longest_streak, user_exercise.streak)

                user_exercise.update_proficiency_model(correct=True)

                bingo([
                    'struggling_problems_correct',
                    'suggested_activity_problems_correct',
                ])

                if user_exercise.progress >= 1.0 and not explicitly_proficient:
                    bingo([
                        'hints_gained_proficiency_all',
                        'struggling_gained_proficiency_all',
                        'suggested_activity_gained_proficiency_all',
                    ])
                    if not user_exercise.has_been_proficient():
                        bingo('hints_gained_new_proficiency')

                    if user_exercise.history_indicates_struggling(struggling_model):
                        bingo('struggling_gained_proficiency_post_struggling')

                    user_exercise.set_proficient(user_data)
                    user_data.reassess_if_necessary()

                    just_earned_proficiency = True
                    problem_log.earned_proficiency = True

            util_badges.update_with_user_exercise(
                user_data,
                user_exercise,
                include_other_badges=True,
                action_cache=last_action_cache.LastActionCache.get_cache_and_push_problem_log(user_data, problem_log))

            # Update phantom user notifications
            util_notify.update(user_data, user_exercise)

            bingo([
                'hints_problems_done',
                'struggling_problems_done',
                'suggested_activity_problems_done',
            ])

        else:
            # Only count wrong answer at most once per problem
            if first_response:
                user_exercise.update_proficiency_model(correct=False)
                bingo([
                    'hints_wrong_problems',
                    'struggling_problems_wrong',
                    'suggested_activity_problems_wrong',
                ])

            if user_exercise.is_struggling(struggling_model):
                bingo('struggling_struggled_binary')

        # If this is the first attempt, update review schedule appropriately
        if attempt_number == 1:
            user_exercise.schedule_review(completed)

        user_exercise_graph = models.UserExerciseGraph.get_and_update(user_data, user_exercise)

        goals_updated = GoalList.update_goals(user_data,
            lambda goal: goal.just_did_exercise(user_data, user_exercise,
                just_earned_proficiency))

        user_data.uservideocss_version += 1
        if user_exercise.progress >= 1.0:
            UserVideoCss.set_completed(user_data.key(), exercise.key(), user_data.uservideocss_version)
        else:
            UserVideoCss.set_started(user_data.key(), exercise.key(), user_data.uservideocss_version)

        # Bulk put
        db.put([user_data, user_exercise, user_exercise_graph.cache])

        # Defer the put of ProblemLog for now, as we think it might be causing hot tablets
        # and want to shift it off to an automatically-retrying task queue.
        # http://ikaisays.com/2011/01/25/app-engine-datastore-tip-monotonically-increasing-values-are-bad/
        deferred.defer(models.commit_problem_log, problem_log,
                       _queue="problem-log-queue")

        if user_data is not None and user_data.coaches:
            # Making a separate queue for the log summaries so we can clearly see how much they are getting used
            deferred.defer(models.commit_log_summary_coaches, problem_log, user_data.coaches,
                       _queue="log-summary-queue",
                       )

        return user_exercise, user_exercise_graph, goals_updated
Ejemplo n.º 7
0
def attempt_problem(user_data, user_exercise, problem_number, attempt_number,
    attempt_content, sha1, seed, completed, count_hints, time_taken,
    exercise_non_summative, problem_type, ip_address):

    if user_exercise and user_exercise.belongs_to(user_data):
        dt_now = datetime.datetime.now()
        exercise = user_exercise.exercise_model

        prev_last_done = user_exercise.last_done
        user_exercise.last_done = dt_now
        user_exercise.seconds_per_fast_problem = exercise.seconds_per_fast_problem
        user_exercise.summative = exercise.summative

        user_data.record_activity(user_exercise.last_done)

        # If a non-admin tries to answer a problem out-of-order, just ignore it
        if problem_number != user_exercise.total_done + 1 and not user_util.is_current_user_developer():
            # Only admins can answer problems out of order.
            raise Exception("Problem number out of order (%s vs %s) for user_id: %s submitting attempt content: %s with seed: %s" % (problem_number, user_exercise.total_done + 1, user_data.user_id, attempt_content, seed))

        if len(sha1) <= 0:
            raise Exception("Missing sha1 hash of problem content.")

        if len(seed) <= 0:
            raise Exception("Missing seed for problem content.")

        if len(attempt_content) > 500:
            raise Exception("Attempt content exceeded maximum length.")

        # Build up problem log for deferred put
        problem_log = models.ProblemLog(
                key_name = "problemlog_%s_%s_%s" % (user_data.key_email, user_exercise.exercise, problem_number),
                user = user_data.user,
                exercise = user_exercise.exercise,
                problem_number = problem_number,
                time_taken = time_taken,
                time_done = dt_now,
                count_hints = count_hints,
                hint_used = count_hints > 0,
                correct = completed and not count_hints and (attempt_number == 1),
                sha1 = sha1,
                seed = seed,
                problem_type = problem_type,
                count_attempts = attempt_number,
                attempts = [attempt_content],
                ip_address = ip_address,
        )

        if exercise.summative:
            problem_log.exercise_non_summative = exercise_non_summative

        first_response = (attempt_number == 1 and count_hints == 0) or (count_hints == 1 and attempt_number == 0)

        if user_exercise.total_done == 0 and first_response:
            user_exercise.bingo_proficiency_model('prof_new_exercises_attempted')

        if user_exercise.total_done > 0 and user_exercise.streak == 0 and first_response:
            user_exercise.bingo_proficiency_model('prof_keep_going_after_wrong')
            bingo('hints_keep_going_after_wrong')

        first_problem_after_proficiency = prev_last_done and user_exercise.proficient_date and (
            abs(prev_last_done - user_exercise.proficient_date) <= datetime.timedelta(seconds=1))

        if first_problem_after_proficiency:
            user_exercise.bingo_proficiency_model('prof_does_problem_just_after_proficiency')

        if completed:

            user_exercise.total_done += 1

            if problem_log.correct:

                proficient = user_data.is_proficient_at(user_exercise.exercise)
                explicitly_proficient = user_data.is_explicitly_proficient_at(user_exercise.exercise)
                suggested = user_data.is_suggested(user_exercise.exercise)
                problem_log.suggested = suggested

                problem_log.points_earned = points.ExercisePointCalculator(user_exercise, suggested, proficient)
                user_data.add_points(problem_log.points_earned)

                # Streak only increments if problem was solved correctly (on first attempt)
                user_exercise.total_correct += 1
                user_exercise.streak += 1
                user_exercise.longest_streak = max(user_exercise.longest_streak, user_exercise.streak)

                user_exercise.update_proficiency_model(correct=True)

                if user_exercise.summative and user_exercise.streak % consts.CHALLENGE_STREAK_BARRIER == 0:
                    user_exercise.streak_start = 0.0

                if user_exercise.progress >= 1.0 and not explicitly_proficient:
                    bingo("hints_gained_proficiency_all")
                    user_exercise.set_proficient(True, user_data)
                    user_data.reassess_if_necessary()

                    problem_log.earned_proficiency = True

                if first_problem_after_proficiency:
                    user_exercise.bingo_proficiency_model('prof_problem_correct_just_after_proficiency')

            util_badges.update_with_user_exercise(
                user_data,
                user_exercise,
                include_other_badges = True,
                action_cache=last_action_cache.LastActionCache.get_cache_and_push_problem_log(user_data, problem_log))

            # Update phantom user notifications
            util_notify.update(user_data, user_exercise)

            user_exercise.bingo_proficiency_model('prof_problems_done')
            bingo('hints_problems_done')

        else:

            if user_exercise.streak == 0:
                # 2+ in a row wrong -> not proficient
                user_exercise.set_proficient(False, user_data)

            # Only count wrong answer at most once per problem
            if first_response:
                user_exercise.update_proficiency_model(correct=False)
                user_exercise.bingo_proficiency_model('prof_wrong_problems')
                bingo('hints_wrong_problems')

        # If this is the first attempt, update review schedule appropriately
        if attempt_number == 1:
            user_exercise.schedule_review(completed)

        user_exercise_graph = models.UserExerciseGraph.get_and_update(user_data, user_exercise)

        # Bulk put
        db.put([user_data, user_exercise, user_exercise_graph.cache])

        # Defer the put of ProblemLog for now, as we think it might be causing hot tablets
        # and want to shift it off to an automatically-retrying task queue.
        # http://ikaisays.com/2011/01/25/app-engine-datastore-tip-monotonically-increasing-values-are-bad/
        deferred.defer(models.commit_problem_log, problem_log,
                       _queue="problem-log-queue",
                       _url="/_ah/queue/deferred_problemlog")

        if user_data is not None and user_data.coaches:
            # Making a separate queue for the log summaries so we can clearly see how much they are getting used
            deferred.defer(models.commit_log_summary_coaches, problem_log, user_data.coaches,
                       _queue = "log-summary-queue",
                       _url = "/_ah/queue/deferred_log_summary") 

        return user_exercise, user_exercise_graph
Ejemplo n.º 8
0
    def get(self):
        user_data = UserData.current()
        user_data_override = self.request_user_data("coach_email")
        if user_util.is_current_user_developer() and user_data_override:
            user_data = user_data_override

        invalid_student = self.request_bool("invalid_student", default=False)

        coach_requests = [
            req.student_requested_identifier
            for req in CoachRequest.get_for_coach(user_data)
            if req.student_requested_data
        ]

        student_lists_models = StudentList.get_for_coach(user_data.key())
        student_lists_list = []
        for student_list in student_lists_models:
            student_lists_list.append({
                'key': str(student_list.key()),
                'name': student_list.name,
            })
        student_lists_dict = dict((g['key'], g) for g in student_lists_list)

        def student_to_dict(s):
            """Convert the UserData s to a dictionary for rendering."""
            return {
                'key':
                str(s.key()),
                'email':
                s.email,
                'username':
                s.username,
                # Note that child users don't have an email.
                'identifier':
                s.username or s.email,
                'nickname':
                s.nickname,
                'profile_root':
                s.profile_root,
                'can_modify_coaches':
                s.can_modify_coaches(),
                'studentLists': [
                    l for l in [
                        student_lists_dict.get(str(list_id))
                        for list_id in s.student_lists
                    ] if l
                ],
            }

        students_data = user_data.get_students_data()
        students = map(student_to_dict, students_data)
        students.sort(key=lambda s: s['nickname'])

        child_accounts = map(student_to_dict, user_data.get_child_users())
        template_values = {
            'students': students,
            'child_accounts': child_accounts,
            'child_account_keyset': set([c['key'] for c in child_accounts]),
            'students_json': json.dumps(students),
            'student_lists': student_lists_list,
            'student_lists_json': json.dumps(student_lists_list),
            'invalid_student': invalid_student,
            'coach_requests': coach_requests,
            'coach_requests_json': json.dumps(coach_requests),
            'selected_nav_link': 'coach',
            'email': user_data.email,
        }
        self.render_jinja2_template('viewstudentlists.html', template_values)
Ejemplo n.º 9
0
def attempt_problem(user_data, user_exercise, problem_number, attempt_number,
                    attempt_content, sha1, seed, completed, count_hints,
                    time_taken, exercise_non_summative, problem_type,
                    ip_address):

    if user_exercise and user_exercise.belongs_to(user_data):
        dt_now = datetime.datetime.now()
        exercise = user_exercise.exercise_model

        prev_last_done = user_exercise.last_done
        user_exercise.last_done = dt_now
        user_exercise.seconds_per_fast_problem = exercise.seconds_per_fast_problem
        user_exercise.summative = exercise.summative

        user_data.record_activity(user_exercise.last_done)

        # If a non-admin tries to answer a problem out-of-order, just ignore it
        if problem_number != user_exercise.total_done + 1 and not user_util.is_current_user_developer(
        ):
            # Only admins can answer problems out of order.
            raise Exception(
                "Problem number out of order (%s vs %s) for user_id: %s submitting attempt content: %s with seed: %s"
                % (problem_number, user_exercise.total_done + 1,
                   user_data.user_id, attempt_content, seed))

        if len(sha1) <= 0:
            raise Exception("Missing sha1 hash of problem content.")

        if len(seed) <= 0:
            raise Exception("Missing seed for problem content.")

        if len(attempt_content) > 500:
            raise Exception("Attempt content exceeded maximum length.")

        # Build up problem log for deferred put
        problem_log = models.ProblemLog(
            key_name="problemlog_%s_%s_%s" %
            (user_data.key_email, user_exercise.exercise, problem_number),
            user=user_data.user,
            exercise=user_exercise.exercise,
            problem_number=problem_number,
            time_taken=time_taken,
            time_done=dt_now,
            count_hints=count_hints,
            hint_used=count_hints > 0,
            correct=completed and not count_hints and (attempt_number == 1),
            sha1=sha1,
            seed=seed,
            problem_type=problem_type,
            count_attempts=attempt_number,
            attempts=[attempt_content],
            ip_address=ip_address,
        )

        if exercise.summative:
            problem_log.exercise_non_summative = exercise_non_summative

        first_response = (attempt_number == 1
                          and count_hints == 0) or (count_hints == 1
                                                    and attempt_number == 0)

        if user_exercise.total_done == 0 and first_response:
            user_exercise.bingo_proficiency_model(
                'prof_new_exercises_attempted')

        if user_exercise.total_done > 0 and user_exercise.streak == 0 and first_response:
            user_exercise.bingo_proficiency_model(
                'prof_keep_going_after_wrong')
            bingo('hints_keep_going_after_wrong')

        first_problem_after_proficiency = prev_last_done and user_exercise.proficient_date and (
            abs(prev_last_done - user_exercise.proficient_date) <=
            datetime.timedelta(seconds=1))

        if first_problem_after_proficiency:
            user_exercise.bingo_proficiency_model(
                'prof_does_problem_just_after_proficiency')

        if completed:

            user_exercise.total_done += 1

            if problem_log.correct:

                proficient = user_data.is_proficient_at(user_exercise.exercise)
                explicitly_proficient = user_data.is_explicitly_proficient_at(
                    user_exercise.exercise)
                suggested = user_data.is_suggested(user_exercise.exercise)
                problem_log.suggested = suggested

                problem_log.points_earned = points.ExercisePointCalculator(
                    user_exercise, suggested, proficient)
                user_data.add_points(problem_log.points_earned)

                # Streak only increments if problem was solved correctly (on first attempt)
                user_exercise.total_correct += 1
                user_exercise.streak += 1
                user_exercise.longest_streak = max(
                    user_exercise.longest_streak, user_exercise.streak)

                user_exercise.update_proficiency_model(correct=True)

                if user_exercise.summative and user_exercise.streak % consts.CHALLENGE_STREAK_BARRIER == 0:
                    user_exercise.streak_start = 0.0

                if user_exercise.progress >= 1.0 and not explicitly_proficient:
                    bingo("hints_gained_proficiency_all")
                    user_exercise.set_proficient(True, user_data)
                    user_data.reassess_if_necessary()

                    problem_log.earned_proficiency = True

                if first_problem_after_proficiency:
                    user_exercise.bingo_proficiency_model(
                        'prof_problem_correct_just_after_proficiency')

            util_badges.update_with_user_exercise(
                user_data,
                user_exercise,
                include_other_badges=True,
                action_cache=last_action_cache.LastActionCache.
                get_cache_and_push_problem_log(user_data, problem_log))

            # Update phantom user notifications
            util_notify.update(user_data, user_exercise)

            user_exercise.bingo_proficiency_model('prof_problems_done')
            bingo('hints_problems_done')

        else:

            if user_exercise.streak == 0:
                # 2+ in a row wrong -> not proficient
                user_exercise.set_proficient(False, user_data)

            # Only count wrong answer at most once per problem
            if first_response:
                user_exercise.update_proficiency_model(correct=False)
                user_exercise.bingo_proficiency_model('prof_wrong_problems')
                bingo('hints_wrong_problems')

        # If this is the first attempt, update review schedule appropriately
        if attempt_number == 1:
            user_exercise.schedule_review(completed)

        user_exercise_graph = models.UserExerciseGraph.get_and_update(
            user_data, user_exercise)

        # Bulk put
        db.put([user_data, user_exercise, user_exercise_graph.cache])

        # Defer the put of ProblemLog for now, as we think it might be causing hot tablets
        # and want to shift it off to an automatically-retrying task queue.
        # http://ikaisays.com/2011/01/25/app-engine-datastore-tip-monotonically-increasing-values-are-bad/
        deferred.defer(models.commit_problem_log,
                       problem_log,
                       _queue="problem-log-queue",
                       _url="/_ah/queue/deferred_problemlog")

        if user_data is not None and user_data.coaches:
            # Making a separate queue for the log summaries so we can clearly see how much they are getting used
            deferred.defer(models.commit_log_summary_coaches,
                           problem_log,
                           user_data.coaches,
                           _queue="log-summary-queue",
                           _url="/_ah/queue/deferred_log_summary")

        return user_exercise, user_exercise_graph
Ejemplo n.º 10
0
 def has_privilege(user_data, points_required):
     return user_util.is_current_user_developer() or \
             user_data.moderator or \
             user_data.points >= points_required
Ejemplo n.º 11
0
 def is_allowed(self):
     return len(self.purge()) < self.hourly_limit or \
             user_util.is_current_user_developer() or \
             self.user_data.moderator
Ejemplo n.º 12
0
 def has_privilege(user_data, points_required):
     return user_util.is_current_user_developer() or \
             user_data.moderator or \
             user_data.points >= points_required
Ejemplo n.º 13
0
def attempt_problem(user_data,
                    user_exercise,
                    problem_number,
                    attempt_number,
                    attempt_content,
                    sha1,
                    seed,
                    completed,
                    count_hints,
                    time_taken,
                    review_mode,
                    topic_mode,
                    problem_type,
                    ip_address,
                    async_problem_log_put=True):

    if user_exercise and user_exercise.belongs_to(user_data):
        dt_now = datetime.datetime.now()
        exercise = user_exercise.exercise_model

        old_graph = user_exercise.get_user_exercise_graph()

        user_exercise.last_done = dt_now
        user_exercise.seconds_per_fast_problem = exercise.seconds_per_fast_problem

        user_data.record_activity(user_exercise.last_done)

        # If a non-admin tries to answer a problem out-of-order, just ignore it
        if problem_number != user_exercise.total_done + 1 and not user_util.is_current_user_developer(
        ):
            # Only admins can answer problems out of order.
            raise custom_exceptions.QuietException(
                "Problem number out of order (%s vs %s) for user_id: %s submitting attempt content: %s with seed: %s"
                % (problem_number, user_exercise.total_done + 1,
                   user_data.user_id, attempt_content, seed))

        if len(sha1) <= 0:
            raise Exception("Missing sha1 hash of problem content.")

        if len(seed) <= 0:
            raise Exception("Missing seed for problem content.")

        if len(attempt_content) > 500:
            raise Exception("Attempt content exceeded maximum length.")

        # Build up problem log for deferred put
        problem_log = exercise_models.ProblemLog(
            key_name=exercise_models.ProblemLog.key_for(
                user_data, user_exercise.exercise, problem_number),
            user=user_data.user,
            exercise=user_exercise.exercise,
            problem_number=problem_number,
            time_taken=time_taken,
            time_done=dt_now,
            count_hints=count_hints,
            hint_used=count_hints > 0,
            correct=completed and not count_hints and (attempt_number == 1),
            sha1=sha1,
            seed=seed,
            problem_type=problem_type,
            count_attempts=attempt_number,
            attempts=[attempt_content],
            ip_address=ip_address,
            review_mode=review_mode,
            topic_mode=topic_mode,
        )

        first_response = (attempt_number == 1
                          and count_hints == 0) or (count_hints == 1
                                                    and attempt_number == 0)

        if user_exercise.total_done > 0 and user_exercise.streak == 0 and first_response:
            bingo('hints_keep_going_after_wrong')

        just_earned_proficiency = False

        # Users can only attempt problems for themselves, so the experiment
        # bucket always corresponds to the one for this current user
        struggling_model = StrugglingExperiment.get_alternative_for_user(
            user_data, current_user=True) or StrugglingExperiment.DEFAULT
        if completed:

            user_exercise.total_done += 1

            if problem_log.correct:

                proficient = user_data.is_proficient_at(user_exercise.exercise)
                explicitly_proficient = user_data.is_explicitly_proficient_at(
                    user_exercise.exercise)
                suggested = user_data.is_suggested(user_exercise.exercise)
                problem_log.suggested = suggested

                problem_log.points_earned = points.ExercisePointCalculator(
                    user_exercise, topic_mode, suggested, proficient)
                user_data.add_points(problem_log.points_earned)

                # Streak only increments if problem was solved correctly (on first attempt)
                user_exercise.total_correct += 1
                user_exercise.streak += 1
                user_exercise.longest_streak = max(
                    user_exercise.longest_streak, user_exercise.streak)

                user_exercise.update_proficiency_model(correct=True)

                bingo([
                    'struggling_problems_correct',
                ])

                if user_exercise.progress >= 1.0 and not explicitly_proficient:
                    bingo([
                        'hints_gained_proficiency_all',
                        'struggling_gained_proficiency_all',
                    ])
                    if not user_exercise.has_been_proficient():
                        bingo('hints_gained_new_proficiency')

                    if user_exercise.history_indicates_struggling(
                            struggling_model):
                        bingo('struggling_gained_proficiency_post_struggling')

                    user_exercise.set_proficient(user_data)
                    user_data.reassess_if_necessary()

                    just_earned_proficiency = True
                    problem_log.earned_proficiency = True

            badges.util_badges.update_with_user_exercise(
                user_data,
                user_exercise,
                include_other_badges=True,
                action_cache=badges.last_action_cache.LastActionCache.
                get_cache_and_push_problem_log(user_data, problem_log))

            # Update phantom user notifications
            phantom_users.util_notify.update(user_data, user_exercise)

            bingo([
                'hints_problems_done',
                'struggling_problems_done',
            ])

        else:
            # Only count wrong answer at most once per problem
            if first_response:
                user_exercise.update_proficiency_model(correct=False)
                bingo([
                    'hints_wrong_problems',
                    'struggling_problems_wrong',
                ])

            if user_exercise.is_struggling(struggling_model):
                bingo('struggling_struggled_binary')

        # If this is the first attempt, update review schedule appropriately
        if attempt_number == 1:
            user_exercise.schedule_review(completed)

        user_exercise_graph = exercise_models.UserExerciseGraph.get_and_update(
            user_data, user_exercise)

        goals_updated = GoalList.update_goals(
            user_data, lambda goal: goal.just_did_exercise(
                user_data, user_exercise, just_earned_proficiency))

        # Bulk put
        db.put([user_data, user_exercise, user_exercise_graph.cache])

        if async_problem_log_put:
            # Defer the put of ProblemLog for now, as we think it might be causing hot tablets
            # and want to shift it off to an automatically-retrying task queue.
            # http://ikaisays.com/2011/01/25/app-engine-datastore-tip-monotonically-increasing-values-are-bad/
            deferred.defer(exercise_models.commit_problem_log,
                           problem_log,
                           _queue="problem-log-queue",
                           _url="/_ah/queue/deferred_problemlog")
        else:
            exercise_models.commit_problem_log(problem_log)

        if user_data is not None and user_data.coaches:
            # Making a separate queue for the log summaries so we can clearly see how much they are getting used
            deferred.defer(video_models.commit_log_summary_coaches,
                           problem_log,
                           user_data.coaches,
                           _queue="log-summary-queue",
                           _url="/_ah/queue/deferred_log_summary")

        return user_exercise, user_exercise_graph, goals_updated
Ejemplo n.º 14
0
 def is_allowed(self):
     return len(self.purge()) < self.hourly_limit or \
             user_util.is_current_user_developer() or \
             self.user_data.moderator