Пример #1
0
    def user_progress_detail(cls, user_id, playlist_id):
        """
        Return a list of video, exercise, and quiz log PlaylistProgressDetail
        objects associated with a specific user and playlist ID.
        """
        user = FacilityUser.objects.get(id=user_id)
        playlist = next((pl for pl in [plist.__dict__ for plist in Playlist.all()] + get_leafed_topics() if pl.get("id") == playlist_id), None)

        pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(playlist)

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(user, pl_video_ids, pl_exercise_ids)

        # Format & append quiz the quiz log, if it exists
        quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(user, (playlist.get("entries") or playlist.get("children")), playlist.get("id"))

        # Finally, sort an ordered list of the playlist entries, with user progress
        # injected where it exists.
        progress_details = list()
        for ent in (playlist.get("entries") or playlist.get("children")):
            entry = {}
            kind = ent.get("entity_kind") or ent.get("kind")
            if kind == "Divider":
                continue
            elif kind == "Video":
                entity_id = get_slug2id_map().get(ent.get("entity_id")) or ent.get("id")
                vid_log = next((vid_log for vid_log in user_vid_logs if vid_log["video_id"] == entity_id), None)
                if vid_log:
                    if vid_log.get("complete"):
                        status = "complete"
                    elif vid_log.get("total_seconds_watched"):
                        status = "inprogress"
                    else:
                        status = "notstarted"

                    leaf_node = get_content_cache().get(vid_log["video_id"])

                    entry = {
                        "id": entity_id,
                        "kind": kind,
                        "status": status,
                        "score": int(float(vid_log.get("points")) / float(750) * 100),
                        "title": leaf_node["title"],
                        "path": leaf_node["path"],
                    }

            elif kind == "Exercise":
                entity_id = (ent.get("entity_id") or ent.get("id"))
                ex_log = next((ex_log for ex_log in user_ex_logs if ex_log["exercise_id"] == entity_id), None)
                if ex_log:
                    if ex_log.get("struggling"):
                        status = "struggling"
                    elif ex_log.get("complete"):
                        status = "complete"
                    elif ex_log.get("attempts"):
                        status = "inprogress"

                    ex_log_id = ex_log.get("exercise_id")
                    leaf_node = get_exercise_cache().get(ex_log_id)

                    entry = {
                        "id": ex_log_id,
                        "kind": kind,
                        "status": status,
                        "score": ex_log.get("streak_progress"),
                        "title": leaf_node["title"],
                        "path": leaf_node["path"],
                    }

            elif kind == "Quiz":
                entity_id = playlist["id"]
                if quiz_log:
                    if quiz_log.complete:
                        if quiz_pct_score <= 59:
                            status = "fail"
                        elif quiz_pct_score <= 79:
                            status = "borderline"
                        else:
                            status = "pass"
                    elif quiz_log.attempts:
                        status = "inprogress"
                    else:
                        status = "notstarted"

                    quiz_log_id = quiz_log.quiz

                    entry = {
                        "id": quiz_log_id,
                        "kind": "Quiz",
                        "status": status,
                        "score": quiz_pct_score,
                        "title": playlist.get("title"),
                        "path": "",
                    }

            if not entry:
                entry = cls.create_empty_entry(entity_id, kind, playlist)

            progress_details.append(cls(**entry))

        return progress_details
Пример #2
0
    def user_progress(cls, user_id):
        """
        Return a list of PlaylistProgress objects associated with the user.
        """
        user = FacilityUser.objects.get(id=user_id)
        all_playlists = [getattr(pl, "__dict__", pl) for pl in Playlist.all() + get_leafed_topics()]

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(user)

        exercise_ids = set([ex_log["exercise_id"] for ex_log in user_ex_logs])
        video_ids = set([get_id2slug_map().get(vid_log["video_id"]) for vid_log in user_vid_logs])
        quiz_log_ids = [ql_id["quiz"] for ql_id in QuizLog.objects.filter(user=user).values("quiz")]
        # Build a list of playlists for which the user has at least one data point
        ## TODO(dylanjbarth) this won't pick up playlists the user is assigned but has not started yet.
        user_playlists = list()
        for p in all_playlists:
            for e in (p.get("entries") or p.get("children")):
                if (e.get("entity_kind") or e.get("kind")) == "Video" or (e.get("entity_kind") or e.get("kind")) == "Exercise":
                    entity_id = convert_leaf_url_to_id((e.get("entity_id") or e.get("id")))

                    if entity_id in exercise_ids or entity_id in video_ids:
                        user_playlists.append(p)
                        break

                elif e.get("entity_kind") == "Quiz":
                    if p.get("id") in quiz_log_ids:
                        user_playlists.append(p)


        # Store stats for each playlist
        user_progress = list()
        for i, p in enumerate(user_playlists):
            # Playlist entry totals
            pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(p)
            n_pl_videos = float(len(pl_video_ids))
            n_pl_exercises = float(len(pl_exercise_ids))

            # Vid & exercise logs in this playlist
            pl_ex_logs = [ex_log for ex_log in user_ex_logs if ex_log["exercise_id"] in pl_exercise_ids]
            pl_vid_logs = [vid_log for vid_log in user_vid_logs if vid_log["video_id"] in pl_video_ids]

            # Compute video stats
            n_vid_complete = len([vid for vid in pl_vid_logs if vid["complete"]])
            n_vid_started = len([vid for vid in pl_vid_logs if (vid["total_seconds_watched"] > 0) and (not vid["complete"])])
            vid_pct_complete = int(float(n_vid_complete) / n_pl_videos * 100) if n_pl_videos else 0
            vid_pct_started = int(float(n_vid_started) / n_pl_videos * 100) if n_pl_videos else 0
            if vid_pct_complete == 100:
                vid_status = "complete"
            elif n_vid_started > 0:
                vid_status = "inprogress"
            else:
                vid_status = "notstarted"

            # Compute exercise stats
            n_ex_mastered = len([ex for ex in pl_ex_logs if ex["complete"]])
            n_ex_started = len([ex for ex in pl_ex_logs if ex["attempts"] > 0])
            n_ex_incomplete = len([ex for ex in pl_ex_logs if (ex["attempts"] > 0 and not ex["complete"])])
            n_ex_struggling = len([ex for ex in pl_ex_logs if ex["struggling"]])
            ex_pct_mastered = int(float(n_ex_mastered) / n_pl_exercises * 100)
            ex_pct_incomplete = int(float(n_ex_incomplete) / n_pl_exercises * 100)
            ex_pct_struggling = int(float(n_ex_struggling) / n_pl_exercises * 100)
            if not n_ex_started:
                ex_status = "notstarted"
            elif ex_pct_struggling > 0:
                # note: we want to help students prioritize areas they need to focus on
                # therefore if they are struggling in this exercise group, we highlight it for them
                ex_status = "struggling"
            elif ex_pct_mastered < 99:
                ex_status = "inprogress"
            else:
                ex_status = "complete"

            # Compute quiz stats
            quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(user, (p.get("entries") or p.get("children")), p.get("id"))
            if quiz_log:
                if quiz_pct_score <= 50:
                    quiz_status = "struggling"
                elif quiz_pct_score <= 79:
                    quiz_status = "borderline"
                else:
                    quiz_status = "complete"
            else:
                quiz_status = "notstarted"

            progress = {
                "title": p.get("title"),
                "id": p.get("id"),
                "tag": p.get("tag"),
                "vid_pct_complete": vid_pct_complete,
                "vid_pct_started": vid_pct_started,
                "vid_status": vid_status,
                "ex_pct_mastered": ex_pct_mastered,
                "ex_pct_incomplete": ex_pct_incomplete,
                "ex_pct_struggling": ex_pct_struggling,
                "ex_status": ex_status,
                "quiz_status": quiz_status,
                "quiz_exists": quiz_exists,
                "quiz_pct_score": quiz_pct_score,
                "n_pl_videos": n_pl_videos,
                "n_pl_exercises": n_pl_exercises,
            }

            try:
                progress["url"] = reverse("view_playlist", kwargs={"playlist_id": p.get("id")})
            except NoReverseMatch:
                progress["url"] = reverse("learn") + p.get("path")

            user_progress.append(cls(**progress))

        return user_progress
Пример #3
0
    def user_progress_detail(cls, user_id, playlist_id, language=None):
        """
        Return a list of video, exercise, and quiz log PlaylistProgressDetail
        objects associated with a specific user and playlist ID.
        """
        if not language:
            language = Settings.get("default_language") or settings.LANGUAGE_CODE

        user = FacilityUser.objects.get(id=user_id)
        playlist = next((pl for pl in get_leafed_topics() if pl.get("id") == playlist_id), None)

        pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(playlist)

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(user, pl_video_ids, pl_exercise_ids)

        # Format & append quiz the quiz log, if it exists
        # quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(user, (playlist.get("entries") or playlist.get("children")), playlist.get("id"))

        # Finally, sort an ordered list of the playlist entries, with user progress
        # injected where it exists.
        progress_details = list()
        for entity_id in playlist.get("children"):
            entry = {}
            leaf_node = get_content_cache(language=language).get(entity_id) or get_exercise_cache(language=language).get(entity_id) or {}
            kind = leaf_node.get("kind")

            status = "notstarted"
            score = 0

            if kind == "Video":
                vid_log = next((vid_log for vid_log in user_vid_logs if vid_log["video_id"] == entity_id), None)
                if vid_log:
                    if vid_log.get("complete"):
                        status = "complete"
                    elif vid_log.get("total_seconds_watched"):
                        status = "inprogress"

                    score = int(float(vid_log.get("points")) / float(750) * 100)

            elif kind == "Exercise":
                ex_log = next((ex_log for ex_log in user_ex_logs if ex_log["exercise_id"] == entity_id), None)
                if ex_log:
                    if ex_log.get("struggling"):
                        status = "struggling"
                    elif ex_log.get("complete"):
                        status = "complete"
                    elif ex_log.get("attempts"):
                        status = "inprogress"

                    score = ex_log.get('streak_progress')

            entry = {
                "id": entity_id,
                "kind": kind,
                "status": status,
                "score": score,
                "title": leaf_node["title"],
                "path": leaf_node["path"],
            }

            progress_details.append(cls(**entry))

        return progress_details
Пример #4
0
    def user_progress(cls, user_id, language=None):
        """
        Return a list of PlaylistProgress objects associated with the user.
        """

        if not language:
            language = Settings.get("default_language") or settings.LANGUAGE_CODE

        user = FacilityUser.objects.get(id=user_id)
        all_playlists = get_leafed_topics(language=language)

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(user)

        exercise_ids = set([ex_log["exercise_id"] for ex_log in user_ex_logs])
        video_ids = set([get_id2slug_map().get(vid_log["video_id"]) for vid_log in user_vid_logs])
        # quiz_log_ids = [ql_id["quiz"] for ql_id in QuizLog.objects.filter(user=user).values("quiz")]
        # Build a list of playlists for which the user has at least one data point
        user_playlists = list()
        for p in all_playlists:
            for e_id in p.get("children"):

                if e_id in exercise_ids or e_id in video_ids:
                    user_playlists.append(p)
                    break

        # Store stats for each playlist
        user_progress = list()
        for i, p in enumerate(user_playlists):
            # Playlist entry totals
            pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(p)
            n_pl_videos = float(len(pl_video_ids))
            n_pl_exercises = float(len(pl_exercise_ids))

            # Vid & exercise logs in this playlist
            pl_ex_logs = [ex_log for ex_log in user_ex_logs if ex_log["exercise_id"] in pl_exercise_ids]
            pl_vid_logs = [vid_log for vid_log in user_vid_logs if vid_log["video_id"] in pl_video_ids]

            # Compute video stats
            n_vid_complete = len([vid for vid in pl_vid_logs if vid["complete"]])
            n_vid_started = len([vid for vid in pl_vid_logs if (vid["total_seconds_watched"] > 0) and (not vid["complete"])])
            vid_pct_complete = int(float(n_vid_complete) / n_pl_videos * 100) if n_pl_videos else 0
            vid_pct_started = int(float(n_vid_started) / n_pl_videos * 100) if n_pl_videos else 0
            if vid_pct_complete == 100:
                vid_status = "complete"
            elif n_vid_started > 0:
                vid_status = "inprogress"
            else:
                vid_status = "notstarted"

            # Compute exercise stats
            n_ex_mastered = len([ex for ex in pl_ex_logs if ex["complete"]])
            n_ex_started = len([ex for ex in pl_ex_logs if ex["attempts"] > 0])
            n_ex_incomplete = len([ex for ex in pl_ex_logs if (ex["attempts"] > 0 and not ex["complete"])])
            n_ex_struggling = len([ex for ex in pl_ex_logs if ex["struggling"]])
            ex_pct_mastered = int(float(n_ex_mastered) / (n_pl_exercises or 1) * 100)
            ex_pct_incomplete = int(float(n_ex_incomplete) / (n_pl_exercises or 1) * 100)
            ex_pct_struggling = int(float(n_ex_struggling) / (n_pl_exercises or 1) * 100)
            if not n_ex_started:
                ex_status = "notstarted"
            elif ex_pct_struggling > 0:
                # note: we want to help students prioritize areas they need to focus on
                # therefore if they are struggling in this exercise group, we highlight it for them
                ex_status = "struggling"
            elif ex_pct_mastered < 99:
                ex_status = "inprogress"
            else:
                ex_status = "complete"

            # Oh Quizzes, we hardly knew ye!
            # TODO (rtibbles): Sort out the status of Quizzes, and either reinstate them or remove them.
            # Compute quiz stats
            # quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(user, (p.get("entries") or p.get("children")), p.get("id"))
            # if quiz_log:
            #     if quiz_pct_score <= 50:
            #         quiz_status = "struggling"
            #     elif quiz_pct_score <= 79:
            #         quiz_status = "borderline"
            #     else:
            #         quiz_status = "complete"
            # else:
            #     quiz_status = "notstarted"

            progress = {
                "title": p.get("title"),
                "id": p.get("id"),
                "tag": p.get("tag"),
                "vid_pct_complete": vid_pct_complete,
                "vid_pct_started": vid_pct_started,
                "vid_status": vid_status,
                "ex_pct_mastered": ex_pct_mastered,
                "ex_pct_incomplete": ex_pct_incomplete,
                "ex_pct_struggling": ex_pct_struggling,
                "ex_status": ex_status,
                # "quiz_status": quiz_status,
                # "quiz_exists": quiz_exists,
                # "quiz_pct_score": quiz_pct_score,
                "n_pl_videos": n_pl_videos,
                "n_pl_exercises": n_pl_exercises,
            }

            try:
                progress["url"] = reverse("view_playlist", kwargs={"playlist_id": p.get("id")})
            except NoReverseMatch:
                progress["url"] = reverse("learn") + p.get("path")

            user_progress.append(cls(**progress))

        return user_progress
Пример #5
0
    def user_progress_detail(cls, user_id, playlist_id):
        """
        Return a list of video, exercise, and quiz log PlaylistProgressDetail
        objects associated with a specific user and playlist ID.
        """
        user = FacilityUser.objects.get(id=user_id)
        playlist = next((pl for pl in get_leafed_topics() if pl.get("id") == playlist_id), None)

        pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(playlist)

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(user, pl_video_ids, pl_exercise_ids)

        # Format & append quiz the quiz log, if it exists
        # quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(user, (playlist.get("entries") or playlist.get("children")), playlist.get("id"))

        # Finally, sort an ordered list of the playlist entries, with user progress
        # injected where it exists.
        progress_details = list()
        for entity_id in playlist.get("children"):
            entry = {}
            leaf_node = get_content_cache().get(entity_id, get_exercise_cache().get(entity_id, {}))
            kind = leaf_node.get("kind")

            if kind == "Video":
                vid_log = next((vid_log for vid_log in user_vid_logs if vid_log["video_id"] == entity_id), None)
                if vid_log:
                    if vid_log.get("complete"):
                        status = "complete"
                    elif vid_log.get("total_seconds_watched"):
                        status = "inprogress"
                    else:
                        status = "notstarted"

                    entry = {
                        "id": entity_id,
                        "kind": kind,
                        "status": status,
                        "score": int(float(vid_log.get("points")) / float(750) * 100),
                        "title": leaf_node["title"],
                        "path": leaf_node["path"],
                    }

            elif kind == "Exercise":
                ex_log = next((ex_log for ex_log in user_ex_logs if ex_log["exercise_id"] == entity_id), None)
                if ex_log:
                    if ex_log.get("struggling"):
                        status = "struggling"
                    elif ex_log.get("complete"):
                        status = "complete"
                    elif ex_log.get("attempts"):
                        status = "inprogress"

                    entry = {
                        "id": entity_id,
                        "kind": kind,
                        "status": status,
                        "score": ex_log.get("streak_progress"),
                        "title": leaf_node["title"],
                        "path": leaf_node["path"],
                    }

            # Oh Quizzes, we hardly knew ye!
            # TODO (rtibbles): Sort out the status of Quizzes, and either reinstate them or remove them.
            # Quizzes were introduced to provide a way of practicing multiple types of exercise at once
            # However, there is currently no way to access them, and the manner for generating them (from the now deprecated Playlist models) is inaccessible
            # elif kind == "Quiz":
            #     entity_id = playlist["id"]
            #     if quiz_log:
            #         if quiz_log.complete:
            #             if quiz_pct_score <= 59:
            #                 status = "fail"
            #             elif quiz_pct_score <= 79:
            #                 status = "borderline"
            #             else:
            #                 status = "pass"
            #         elif quiz_log.attempts:
            #             status = "inprogress"
            #         else:
            #             status = "notstarted"

            #         quiz_log_id = quiz_log.quiz

            #         entry = {
            #             "id": quiz_log_id,
            #             "kind": "Quiz",
            #             "status": status,
            #             "score": quiz_pct_score,
            #             "title": playlist.get("title"),
            #             "path": "",
            #         }

            if not entry:
                entry = cls.create_empty_entry(entity_id, kind, playlist)

            progress_details.append(cls(**entry))

        return progress_details
Пример #6
0
    def user_progress(cls, user_id, language=None):
        """
        Return a list of PlaylistProgress objects associated with the user.
        """

        if not language:
            language = Settings.get(
                "default_language") or settings.LANGUAGE_CODE

        user = FacilityUser.objects.get(id=user_id)
        all_playlists = get_leafed_topics(language=language)

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(user)

        exercise_ids = set([ex_log["exercise_id"] for ex_log in user_ex_logs])
        video_ids = set([
            get_id2slug_map().get(vid_log["video_id"])
            for vid_log in user_vid_logs
        ])
        # quiz_log_ids = [ql_id["quiz"] for ql_id in QuizLog.objects.filter(user=user).values("quiz")]
        # Build a list of playlists for which the user has at least one data point
        user_playlists = list()
        for p in all_playlists:
            for e_id in p.get("children"):

                if e_id in exercise_ids or e_id in video_ids:
                    user_playlists.append(p)
                    break

        # Store stats for each playlist
        user_progress = list()
        for i, p in enumerate(user_playlists):
            # Playlist entry totals
            pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(p)
            n_pl_videos = float(len(pl_video_ids))
            n_pl_exercises = float(len(pl_exercise_ids))

            # Vid & exercise logs in this playlist
            pl_ex_logs = [
                ex_log for ex_log in user_ex_logs
                if ex_log["exercise_id"] in pl_exercise_ids
            ]
            pl_vid_logs = [
                vid_log for vid_log in user_vid_logs
                if vid_log["video_id"] in pl_video_ids
            ]

            # Compute video stats
            n_vid_complete = len(
                [vid for vid in pl_vid_logs if vid["complete"]])
            n_vid_started = len([
                vid for vid in pl_vid_logs
                if (vid["total_seconds_watched"] > 0) and (not vid["complete"])
            ])
            vid_pct_complete = int(float(n_vid_complete) / n_pl_videos *
                                   100) if n_pl_videos else 0
            vid_pct_started = int(float(n_vid_started) / n_pl_videos *
                                  100) if n_pl_videos else 0
            if vid_pct_complete == 100:
                vid_status = "complete"
            elif n_vid_started > 0:
                vid_status = "inprogress"
            else:
                vid_status = "notstarted"

            # Compute exercise stats
            n_ex_mastered = len([ex for ex in pl_ex_logs if ex["complete"]])
            n_ex_started = len([ex for ex in pl_ex_logs if ex["attempts"] > 0])
            n_ex_incomplete = len([
                ex for ex in pl_ex_logs
                if (ex["attempts"] > 0 and not ex["complete"])
            ])
            n_ex_struggling = len(
                [ex for ex in pl_ex_logs if ex["struggling"]])
            ex_pct_mastered = int(
                float(n_ex_mastered) / (n_pl_exercises or 1) * 100)
            ex_pct_incomplete = int(
                float(n_ex_incomplete) / (n_pl_exercises or 1) * 100)
            ex_pct_struggling = int(
                float(n_ex_struggling) / (n_pl_exercises or 1) * 100)
            if not n_ex_started:
                ex_status = "notstarted"
            elif ex_pct_struggling > 0:
                # note: we want to help students prioritize areas they need to focus on
                # therefore if they are struggling in this exercise group, we highlight it for them
                ex_status = "struggling"
            elif ex_pct_mastered < 99:
                ex_status = "inprogress"
            else:
                ex_status = "complete"

            # Oh Quizzes, we hardly knew ye!
            # TODO (rtibbles): Sort out the status of Quizzes, and either reinstate them or remove them.
            # Compute quiz stats
            # quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(user, (p.get("entries") or p.get("children")), p.get("id"))
            # if quiz_log:
            #     if quiz_pct_score <= 50:
            #         quiz_status = "struggling"
            #     elif quiz_pct_score <= 79:
            #         quiz_status = "borderline"
            #     else:
            #         quiz_status = "complete"
            # else:
            #     quiz_status = "notstarted"

            progress = {
                "title": p.get("title"),
                "id": p.get("id"),
                "tag": p.get("tag"),
                "vid_pct_complete": vid_pct_complete,
                "vid_pct_started": vid_pct_started,
                "vid_status": vid_status,
                "ex_pct_mastered": ex_pct_mastered,
                "ex_pct_incomplete": ex_pct_incomplete,
                "ex_pct_struggling": ex_pct_struggling,
                "ex_status": ex_status,
                # "quiz_status": quiz_status,
                # "quiz_exists": quiz_exists,
                # "quiz_pct_score": quiz_pct_score,
                "n_pl_videos": n_pl_videos,
                "n_pl_exercises": n_pl_exercises,
            }

            try:
                progress["url"] = reverse("view_playlist",
                                          kwargs={"playlist_id": p.get("id")})
            except NoReverseMatch:
                progress["url"] = reverse("learn") + p.get("path")

            user_progress.append(cls(**progress))

        return user_progress
Пример #7
0
    def user_progress_detail(cls, user_id, playlist_id, language=None):
        """
        Return a list of video, exercise, and quiz log PlaylistProgressDetail
        objects associated with a specific user and playlist ID.
        """
        if not language:
            language = Settings.get(
                "default_language") or settings.LANGUAGE_CODE

        user = FacilityUser.objects.get(id=user_id)
        playlist = next(
            (pl for pl in get_leafed_topics() if pl.get("id") == playlist_id),
            None)

        pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(playlist)

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(
            user, pl_video_ids, pl_exercise_ids)

        # Format & append quiz the quiz log, if it exists
        # quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(user, (playlist.get("entries") or playlist.get("children")), playlist.get("id"))

        # Finally, sort an ordered list of the playlist entries, with user progress
        # injected where it exists.
        progress_details = list()
        for entity_id in playlist.get("children"):
            entry = {}
            leaf_node = get_content_cache(
                language=language).get(entity_id) or get_exercise_cache(
                    language=language).get(entity_id) or {}
            kind = leaf_node.get("kind")

            status = "notstarted"
            score = 0

            if kind == "Video":
                vid_log = next((vid_log for vid_log in user_vid_logs
                                if vid_log["video_id"] == entity_id), None)
                if vid_log:
                    if vid_log.get("complete"):
                        status = "complete"
                    elif vid_log.get("total_seconds_watched"):
                        status = "inprogress"

                    score = int(
                        float(vid_log.get("points")) / float(750) * 100)

            elif kind == "Exercise":
                ex_log = next((ex_log for ex_log in user_ex_logs
                               if ex_log["exercise_id"] == entity_id), None)
                if ex_log:
                    if ex_log.get("struggling"):
                        status = "struggling"
                    elif ex_log.get("complete"):
                        status = "complete"
                    elif ex_log.get("attempts"):
                        status = "inprogress"

                    score = ex_log.get('streak_progress')

            entry = {
                "id": entity_id,
                "kind": kind,
                "status": status,
                "score": score,
                "title": leaf_node["title"],
                "path": leaf_node["path"],
            }

            progress_details.append(cls(**entry))

        return progress_details
Пример #8
0
    def user_progress_detail(cls, user_id, playlist_id):
        """
        Return a list of video, exercise, and quiz log PlaylistProgressDetail
        objects associated with a specific user and playlist ID.
        """
        user = FacilityUser.objects.get(id=user_id)
        playlist = next(
            (
                pl
                for pl in [plist.__dict__ for plist in Playlist.all()] + get_leafed_topics()
                if pl.get("id") == playlist_id
            ),
            None,
        )

        pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(playlist)

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(user, pl_video_ids, pl_exercise_ids)

        # Format & append quiz the quiz log, if it exists
        quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(
            user, (playlist.get("entries") or playlist.get("children")), playlist.get("id")
        )

        # Finally, sort an ordered list of the playlist entries, with user progress
        # injected where it exists.
        progress_details = list()
        for ent in playlist.get("entries") or playlist.get("children"):
            entry = {}
            kind = ent.get("entity_kind") or ent.get("kind")
            if kind == "Divider":
                continue
            elif kind == "Video":
                entity_id = get_slug2id_map().get(ent.get("entity_id")) or ent.get("id")
                vid_log = next((vid_log for vid_log in user_vid_logs if vid_log["video_id"] == entity_id), None)
                if vid_log:
                    if vid_log.get("complete"):
                        status = "complete"
                    elif vid_log.get("total_seconds_watched"):
                        status = "inprogress"
                    else:
                        status = "notstarted"

                    leaf_node = get_content_cache().get(vid_log["video_id"])

                    entry = {
                        "id": entity_id,
                        "kind": kind,
                        "status": status,
                        "score": int(float(vid_log.get("points")) / float(750) * 100),
                        "title": leaf_node["title"],
                        "path": leaf_node["path"],
                    }

            elif kind == "Exercise":
                entity_id = ent.get("entity_id") or ent.get("id")
                ex_log = next((ex_log for ex_log in user_ex_logs if ex_log["exercise_id"] == entity_id), None)
                if ex_log:
                    if ex_log.get("struggling"):
                        status = "struggling"
                    elif ex_log.get("complete"):
                        status = "complete"
                    elif ex_log.get("attempts"):
                        status = "inprogress"

                    ex_log_id = ex_log.get("exercise_id")
                    leaf_node = get_exercise_cache().get(ex_log_id)

                    entry = {
                        "id": ex_log_id,
                        "kind": kind,
                        "status": status,
                        "score": ex_log.get("streak_progress"),
                        "title": leaf_node["title"],
                        "path": leaf_node["path"],
                    }

            elif kind == "Quiz":
                entity_id = playlist["id"]
                if quiz_log:
                    if quiz_log.complete:
                        if quiz_pct_score <= 59:
                            status = "fail"
                        elif quiz_pct_score <= 79:
                            status = "borderline"
                        else:
                            status = "pass"
                    elif quiz_log.attempts:
                        status = "inprogress"
                    else:
                        status = "notstarted"

                    quiz_log_id = quiz_log.quiz

                    entry = {
                        "id": quiz_log_id,
                        "kind": "Quiz",
                        "status": status,
                        "score": quiz_pct_score,
                        "title": playlist.get("title"),
                        "path": "",
                    }

            if not entry:
                entry = cls.create_empty_entry(entity_id, kind, playlist)

            progress_details.append(cls(**entry))

        return progress_details
Пример #9
0
    def user_progress(cls, user_id):
        """
        Return a list of PlaylistProgress objects associated with the user.
        """
        user = FacilityUser.objects.get(id=user_id)
        all_playlists = [getattr(pl, "__dict__", pl) for pl in Playlist.all() + get_leafed_topics()]

        # Retrieve video, exercise, and quiz logs that appear in this playlist
        user_vid_logs, user_ex_logs = cls.get_user_logs(user)

        exercise_ids = set([ex_log["exercise_id"] for ex_log in user_ex_logs])
        video_ids = set([get_id2slug_map().get(vid_log["video_id"]) for vid_log in user_vid_logs])
        quiz_log_ids = [ql_id["quiz"] for ql_id in QuizLog.objects.filter(user=user).values("quiz")]
        # Build a list of playlists for which the user has at least one data point
        ## TODO(dylanjbarth) this won't pick up playlists the user is assigned but has not started yet.
        user_playlists = list()
        for p in all_playlists:
            for e in p.get("entries") or p.get("children"):
                if (e.get("entity_kind") or e.get("kind")) == "Video" or (
                    e.get("entity_kind") or e.get("kind")
                ) == "Exercise":
                    entity_id = convert_leaf_url_to_id((e.get("entity_id") or e.get("id")))

                    if entity_id in exercise_ids or entity_id in video_ids:
                        user_playlists.append(p)
                        break

                elif e.get("entity_kind") == "Quiz":
                    if p.get("id") in quiz_log_ids:
                        user_playlists.append(p)

        # Store stats for each playlist
        user_progress = list()
        for i, p in enumerate(user_playlists):
            # Playlist entry totals
            pl_video_ids, pl_exercise_ids = cls.get_playlist_entry_ids(p)
            n_pl_videos = float(len(pl_video_ids))
            n_pl_exercises = float(len(pl_exercise_ids))

            # Vid & exercise logs in this playlist
            pl_ex_logs = [ex_log for ex_log in user_ex_logs if ex_log["exercise_id"] in pl_exercise_ids]
            pl_vid_logs = [vid_log for vid_log in user_vid_logs if vid_log["video_id"] in pl_video_ids]

            # Compute video stats
            n_vid_complete = len([vid for vid in pl_vid_logs if vid["complete"]])
            n_vid_started = len(
                [vid for vid in pl_vid_logs if (vid["total_seconds_watched"] > 0) and (not vid["complete"])]
            )
            vid_pct_complete = int(float(n_vid_complete) / n_pl_videos * 100) if n_pl_videos else 0
            vid_pct_started = int(float(n_vid_started) / n_pl_videos * 100) if n_pl_videos else 0
            if vid_pct_complete == 100:
                vid_status = "complete"
            elif n_vid_started > 0:
                vid_status = "inprogress"
            else:
                vid_status = "notstarted"

            # Compute exercise stats
            n_ex_mastered = len([ex for ex in pl_ex_logs if ex["complete"]])
            n_ex_started = len([ex for ex in pl_ex_logs if ex["attempts"] > 0])
            n_ex_incomplete = len([ex for ex in pl_ex_logs if (ex["attempts"] > 0 and not ex["complete"])])
            n_ex_struggling = len([ex for ex in pl_ex_logs if ex["struggling"]])
            ex_pct_mastered = int(float(n_ex_mastered) / n_pl_exercises * 100)
            ex_pct_incomplete = int(float(n_ex_incomplete) / n_pl_exercises * 100)
            ex_pct_struggling = int(float(n_ex_struggling) / n_pl_exercises * 100)
            if not n_ex_started:
                ex_status = "notstarted"
            elif ex_pct_struggling > 0:
                # note: we want to help students prioritize areas they need to focus on
                # therefore if they are struggling in this exercise group, we highlight it for them
                ex_status = "struggling"
            elif ex_pct_mastered < 99:
                ex_status = "inprogress"
            else:
                ex_status = "complete"

            # Compute quiz stats
            quiz_exists, quiz_log, quiz_pct_score = cls.get_quiz_log(
                user, (p.get("entries") or p.get("children")), p.get("id")
            )
            if quiz_log:
                if quiz_pct_score <= 50:
                    quiz_status = "struggling"
                elif quiz_pct_score <= 79:
                    quiz_status = "borderline"
                else:
                    quiz_status = "complete"
            else:
                quiz_status = "notstarted"

            progress = {
                "title": p.get("title"),
                "id": p.get("id"),
                "tag": p.get("tag"),
                "vid_pct_complete": vid_pct_complete,
                "vid_pct_started": vid_pct_started,
                "vid_status": vid_status,
                "ex_pct_mastered": ex_pct_mastered,
                "ex_pct_incomplete": ex_pct_incomplete,
                "ex_pct_struggling": ex_pct_struggling,
                "ex_status": ex_status,
                "quiz_status": quiz_status,
                "quiz_exists": quiz_exists,
                "quiz_pct_score": quiz_pct_score,
                "n_pl_videos": n_pl_videos,
                "n_pl_exercises": n_pl_exercises,
            }

            try:
                progress["url"] = reverse("view_playlist", kwargs={"playlist_id": p.get("id")})
            except NoReverseMatch:
                progress["url"] = reverse("learn") + p.get("path")

            user_progress.append(cls(**progress))

        return user_progress