diff --git a/community/lms/api.py b/community/lms/api.py index 089ad86d..b60d2d13 100644 --- a/community/lms/api.py +++ b/community/lms/api.py @@ -29,13 +29,13 @@ def submit_solution(exercise, code): return {"name": doc.name, "creation": doc.creation} @frappe.whitelist() -def save_current_lesson(batch_name, lesson_name): +def save_current_lesson(course_name, lesson_name): """Saves the current lesson for a student/mentor. """ name = frappe.get_value( doctype="LMS Batch Membership", filters={ - "batch": batch_name, + "course": course_name, "member": frappe.session.user }, fieldname="name") diff --git a/community/lms/doctype/lesson/lesson.py b/community/lms/doctype/lesson/lesson.py index c96d3315..53fe5d89 100644 --- a/community/lms/doctype/lesson/lesson.py +++ b/community/lms/doctype/lesson/lesson.py @@ -55,11 +55,11 @@ class Lesson(Document): return @frappe.whitelist() -def save_progress(lesson, batch): +def save_progress(lesson, course): if not frappe.db.exists("LMS Batch Membership", { "member": frappe.session.user, - "batch": batch + "course": course }): return if frappe.db.exists("LMS Course Progress", diff --git a/community/lms/doctype/lms_batch/lms_batch.py b/community/lms/doctype/lms_batch/lms_batch.py index 391eac7e..8ed2b990 100644 --- a/community/lms/doctype/lms_batch/lms_batch.py +++ b/community/lms/doctype/lms_batch/lms_batch.py @@ -19,15 +19,7 @@ class LMSBatch(Document): frappe.throw(_("You are not a mentor of the course {0}").format(course.title)) def after_insert(self): - create_membership(batch=self.name, member_type="Mentor") - - def get_mentors(self): - memberships = frappe.get_all( - "LMS Batch Membership", - {"batch": self.name, "member_type": "Mentor"}, - ["member"]) - member_names = [m['member'] for m in memberships] - return find_all("User", name=["IN", member_names]) + create_membership(batch=self.name, course=self.course, member_type="Mentor") def is_member(self, email, member_type=None): """Checks if a person is part of a batch. @@ -43,16 +35,6 @@ class LMSBatch(Document): filters['member_type'] = member_type return frappe.db.exists("LMS Batch Membership", filters) - def get_students(self): - """Returns (email, full_name, username) of all the students of this batch as a list of dict. - """ - memberships = frappe.get_all( - "LMS Batch Membership", - {"batch": self.name, "member_type": "Student"}, - ["member"]) - member_names = [m['member'] for m in memberships] - return find_all("User", name=["IN", member_names]) - def get_messages(self): messages = frappe.get_all("LMS Message", {"batch": self.name}, ["*"], order_by="creation") for message in messages: diff --git a/community/lms/doctype/lms_batch_membership/lms_batch_membership.json b/community/lms/doctype/lms_batch_membership/lms_batch_membership.json index 6a0cc143..ba43cd69 100644 --- a/community/lms/doctype/lms_batch_membership/lms_batch_membership.json +++ b/community/lms/doctype/lms_batch_membership/lms_batch_membership.json @@ -13,8 +13,7 @@ "course", "member_type", "role", - "current_lesson", - "is_current" + "current_lesson" ], "fields": [ { @@ -81,19 +80,11 @@ "fieldtype": "Data", "label": "Memeber Username", "read_only": 1 - }, - { - "default": "0", - "fieldname": "is_current", - "fieldtype": "Check", - "hidden": 1, - "label": "Is Currently Being Used", - "read_only": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-06-14 10:24:35.425498", + "modified": "2021-06-21 12:10:28.808803", "modified_by": "Administrator", "module": "LMS", "name": "LMS Batch Membership", diff --git a/community/lms/doctype/lms_batch_membership/lms_batch_membership.py b/community/lms/doctype/lms_batch_membership/lms_batch_membership.py index e64cbeed..5dd33f09 100644 --- a/community/lms/doctype/lms_batch_membership/lms_batch_membership.py +++ b/community/lms/doctype/lms_batch_membership/lms_batch_membership.py @@ -14,18 +14,22 @@ class LMSBatchMembership(Document): self.validate_membership_in_different_batch_same_course() def validate_membership_in_same_batch(self): + filters={ + "member": self.member, + "course": self.course, + "name": ["!=", self.name] + } + if self.batch: + filters["batch"] = self.batch previous_membership = frappe.db.get_value("LMS Batch Membership", - filters={ - "member": self.member, - "batch": self.batch, - "name": ["!=", self.name] - }, + filters, fieldname=["member_type","member"], as_dict=1) if previous_membership: member_name = frappe.db.get_value("User", self.member, "full_name") - frappe.throw(_("{0} is already a {1} of {2}").format(member_name, previous_membership.member_type, self.batch)) + course_title = frappe.db.get_value("LMS Course", self.course, "title") + frappe.throw(_("{0} is already a {1} of the course {2}").format(member_name, previous_membership.member_type, course_title)) def validate_membership_in_different_batch_same_course(self): course = frappe.db.get_value("LMS Batch", self.batch, "course") @@ -44,10 +48,11 @@ class LMSBatchMembership(Document): frappe.throw(_("{0} is already a {1} of {2} course through {3} batch").format(member_name, membership.member_type, course, membership.batch)) @frappe.whitelist() -def create_membership(batch, member=None, member_type="Student", role="Member"): +def create_membership(course, batch=None, member=None, member_type="Student", role="Member"): frappe.get_doc({ "doctype": "LMS Batch Membership", "batch": batch, + "course": course, "role": role, "member_type": member_type, "member": member or frappe.session.user diff --git a/community/lms/doctype/lms_course/lms_course.json b/community/lms/doctype/lms_course/lms_course.json index 187a24e1..8b46e3ec 100644 --- a/community/lms/doctype/lms_course/lms_course.json +++ b/community/lms/doctype/lms_course/lms_course.json @@ -22,6 +22,7 @@ "field_order": [ "title", "is_published", + "disable_self_learning", "column_break_3", "short_code", "video_link", @@ -73,6 +74,12 @@ "fieldtype": "Small Text", "label": "Short Introduction", "reqd": 1 + }, + { + "default": "0", + "fieldname": "disable_self_learning", + "fieldtype": "Check", + "label": "Disable Self Learning" } ], "index_web_pages_for_search": 1, @@ -99,7 +106,7 @@ "link_fieldname": "course" } ], - "modified": "2021-06-01 04:36:45.696776", + "modified": "2021-06-21 11:34:04.552376", "modified_by": "Administrator", "module": "LMS", "name": "LMS Course", diff --git a/community/lms/doctype/lms_course/lms_course.py b/community/lms/doctype/lms_course/lms_course.py index c6dce996..7f77cd5d 100644 --- a/community/lms/doctype/lms_course/lms_course.py +++ b/community/lms/doctype/lms_course/lms_course.py @@ -82,11 +82,11 @@ class LMSCourse(Document): """ if not email: return False - return frappe.db.exists({ - "doctype": "LMS Course Mentor Mapping", - "course": self.name, - "mentor": email - }) + return frappe.db.count("LMS Course Mentor Mapping", + { + "course": self.name, + "mentor": email + }) def get_student_batch(self, email): """Returns the batch the given student is part of. @@ -192,22 +192,53 @@ class LMSCourse(Document): return return f"/courses/{self.name}/learn/{lesson_number}" - def get_current_batch(self, member): - current_membership = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name, "is_current": 1}, pluck="batch") - print(current_membership, member, self.name, frappe.session.user) - if len(current_membership): - return current_membership[0] - print(frappe.db.get_value("LMS Batch Membership", {"member": member, "course": self.name}, "batch")) - return frappe.db.get_value("LMS Batch Membership", {"member": member, "course": self.name}, "batch") + def get_membership(self, member, batch=None): + filters = { + "member": member, + "course": self.name + } + if batch: + filters["batch"] = batch + return frappe.db.get_value("LMS Batch Membership", filters, ["name","batch", "current_lesson"], as_dict=True) def get_all_memberships(self, member=frappe.session.user): - print(member, frappe.session.user) - all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name}, ["batch", "is_current"]) - print(all_memberships) + all_memberships = frappe.get_all("LMS Batch Membership", {"member": member, "course": self.name}, ["batch"]) for membership in all_memberships: membership.batch_title = frappe.db.get_value("LMS Batch", membership.batch, "title") + print(all_memberships) return all_memberships + def get_mentors(self, batch=None): + filters = { + "course": self.name, + "member_type": "Mentor" + } + if batch: + filters["batch"] = batch + + memberships = frappe.get_all( + "LMS Batch Membership", + filters, + ["member"]) + member_names = [m['member'] for m in memberships] + return find_all("User", name=["IN", member_names]) + + def get_students(self, batch=None): + """Returns (email, full_name, username) of all the students of this batch as a list of dict. + """ + filters = { + "course": self.name, + "member_type": "Student" + } + if batch: + filters["batch"] = batch + memberships = frappe.get_all( + "LMS Batch Membership", + filters, + ["member"]) + member_names = [m['member'] for m in memberships] + return find_all("User", name=["IN", member_names]) + def get_outline(self): return CourseOutline(self) diff --git a/community/lms/doctype/lms_course/test_lms_course.py b/community/lms/doctype/lms_course/test_lms_course.py index db17c8de..81193128 100644 --- a/community/lms/doctype/lms_course/test_lms_course.py +++ b/community/lms/doctype/lms_course/test_lms_course.py @@ -26,7 +26,6 @@ class TestLMSCourse(unittest.TestCase): course = self.new_course("Test Course") assert course.title == "Test Course" assert course.name == "test-course" - assert course.get_mentors() == [] def test_find_all(self): courses = LMSCourse.find_all() diff --git a/community/lms/widgets/BatchTabs.html b/community/lms/widgets/BatchTabs.html index a6778594..1b6d61d9 100644 --- a/community/lms/widgets/BatchTabs.html +++ b/community/lms/widgets/BatchTabs.html @@ -4,74 +4,62 @@ {{ course.title }} {% endif %} {% set all_memberships = course.get_all_memberships() %} {% if all_memberships | length > 1 %} - {% endif %} -{% if not batch %} +{% if not membership %} {% set display_class = "hide" %} {% else %} {% set display_class = "" %} {% endif %} + +{% block script %} +{% endblock %} diff --git a/community/lms/widgets/ChapterTeaser.html b/community/lms/widgets/ChapterTeaser.html index 505c2d96..b4a0179f 100644 --- a/community/lms/widgets/ChapterTeaser.html +++ b/community/lms/widgets/ChapterTeaser.html @@ -8,7 +8,7 @@ {% for lesson in chapter.get_lessons() %}
{{ lesson.title }} {% if show_progress and not course.is_mentor(frappe.session.user) and lesson.get_progress() %} {{ lesson.get_progress() }} diff --git a/community/lms/widgets/RenderBatch.html b/community/lms/widgets/RenderBatch.html index cf23b7a4..bb70c2c5 100644 --- a/community/lms/widgets/RenderBatch.html +++ b/community/lms/widgets/RenderBatch.html @@ -7,7 +7,7 @@
Starting {{frappe.utils.format_date(batch.start_date, "medium")}}
mentors
- {% for m in batch.get_mentors() %} + {% for m in course.get_mentors(batch.name) %}
{{ widgets.Avatar(member=m, avatar_class="avatar-medium" ) }} {{m.full_name}} @@ -18,10 +18,10 @@
{% if can_manage %} - Manage {% elif can_join %} - {% endif %}
diff --git a/community/public/css/style.css b/community/public/css/style.css index 448b6431..30d07de8 100644 --- a/community/public/css/style.css +++ b/community/public/css/style.css @@ -23,6 +23,7 @@ --cta-color: var(--c4); --send-message: var(--c7); --received-message: var(--c8); + --control-bg: var(--gray-100); } body { diff --git a/community/www/batch/discuss.html b/community/www/batch/discuss.html index a31b0f92..75a6963f 100644 --- a/community/www/batch/discuss.html +++ b/community/www/batch/discuss.html @@ -11,7 +11,7 @@ {% block content %}
- {{ widgets.BatchTabs(course=course, batch=batch) }} + {{ widgets.BatchTabs(course=course, membership=membership) }}
{{ widgets.BatchHeader(batch_name=batch.title, member_count=member_count)}}
    diff --git a/community/www/batch/discuss.py b/community/www/batch/discuss.py index 95ffcb34..dd9718db 100644 --- a/community/www/batch/discuss.py +++ b/community/www/batch/discuss.py @@ -4,5 +4,5 @@ from . import utils def get_context(context): utils.get_common_context(context) context.messages = context.batch.get_messages() - if not context.batch: + if not context.membership: utils.redirect_to_lesson(context.course) diff --git a/community/www/batch/home.html b/community/www/batch/home.html index 4b930430..d53888d4 100644 --- a/community/www/batch/home.html +++ b/community/www/batch/home.html @@ -8,15 +8,13 @@ {% endblock %} {% block content %} -{% set invite_link = frappe.utils.get_url() + "/courses/" + course.name + "/join?batch=" + batch.name %}
    - {{ widgets.BatchTabs(course=course, batch=batch) }} - + {{ widgets.BatchTabs(course=course, membership=membership) }}
    {{ widgets.CourseOutline(course=course, batch=batch, show_link=True, show_progress=True) }}
    + + {% if batch %}

    Batch Schedule

    {{ widgets.RenderBatch(course=course, batch=batch) }} @@ -28,8 +26,9 @@ {{ frappe.utils.md_to_html(batch.description) }}
    {% endif %} - + {% endif %} {% if course.is_mentor(frappe.session.user) %} + {% set invite_link = frappe.utils.get_url() + "/courses/" + course.name + "/join?batch=" + batch.name %}

    Invite Members

    Get Batch Invitation diff --git a/community/www/batch/home.py b/community/www/batch/home.py index bb2a53a2..48be81c9 100644 --- a/community/www/batch/home.py +++ b/community/www/batch/home.py @@ -3,5 +3,3 @@ from . import utils def get_context(context): utils.get_common_context(context) - if not context.batch: - utils.redirect_to_lesson(context.course) diff --git a/community/www/batch/join.html b/community/www/batch/join.html index 0585d314..57796295 100644 --- a/community/www/batch/join.html +++ b/community/www/batch/join.html @@ -51,7 +51,8 @@ frappe.ready(() => { frappe.call({ "method": "community.lms.doctype.lms_batch_membership.lms_batch_membership.create_membership", "args": { - "batch": "{{ batch.name }}" + "batch": {{ batch.name }}, + "course": {{ batch.course }} }, "callback": (data) => { if (data.message == "OK") { @@ -66,20 +67,6 @@ frappe.ready(() => { } }) }) - - $("#batch-home").click((e) => { - frappe.call({ - method: "community.lms.doctype.lms_batch_membership.lms_batch_membership.update_current_membership", - args: { - "batch": "{{ batch.name }}", - "course": "{{ batch.course}}", - "member": frappe.session.user - }, - callback: (data) => { - window.location.href = "/courses/{{ batch.course }}/home" - } - }) - }) }) {% endblock %} diff --git a/community/www/batch/learn.html b/community/www/batch/learn.html index da9834b4..a65dc4e0 100644 --- a/community/www/batch/learn.html +++ b/community/www/batch/learn.html @@ -13,7 +13,7 @@ {% for ext in page_extensions %} - {{ ext.render_header() }} +{{ ext.render_header() }} {% endfor %} {% endblock %} @@ -22,17 +22,18 @@ {% block content %}
    - {{ widgets.BatchTabs(course=course, batch=batch) }} + {{ widgets.BatchTabs(course=course, membership=membership) }}
    -

    {{ lesson.title }}

    +

    {{ lesson.title }}

    - {% if batch or lesson.include_in_preview %} + {% if membership or lesson.include_in_preview %} {{ lesson.render_html() }} {% else %}
    {% endif %} @@ -61,7 +62,6 @@ {%- block script %} {{ super() }} {% for ext in page_extensions %} - {{ ext.render_footer() }} +{{ ext.render_footer() }} {% endfor %} - {%- endblock %} diff --git a/community/www/batch/learn.js b/community/www/batch/learn.js index 05fbcb33..e15c5c5c 100644 --- a/community/www/batch/learn.js +++ b/community/www/batch/learn.js @@ -1,17 +1,17 @@ frappe.ready(() => { - if ($(".title").attr("data-batch") && !$(".title").hasClass("is_mentor")) { + if ($(".title").attr("data-membership") && !$(".title").hasClass("is_mentor")) { frappe.call({ method: "community.lms.doctype.lesson.lesson.save_progress", args: { - lesson: $(".title").attr("data-name"), - batch: $(".title").attr("data-batch") + lesson: $(".title").attr("data-lesson"), + course: $(".title").attr("data-course") } }) } - if ($(".title").attr("data-batch")) { + if ($(".title").attr("data-membership")) { frappe.call("community.lms.api.save_current_lesson", { - "batch_name": $(".title").attr("data-batch"), - "lesson_name": $(".title").attr("data-name") + course_name: $(".title").attr("data-course"), + lesson_name: $(".title").attr("data-lesson") }) } }) diff --git a/community/www/batch/learn.py b/community/www/batch/learn.py index 241bc1e7..062ff364 100644 --- a/community/www/batch/learn.py +++ b/community/www/batch/learn.py @@ -18,7 +18,7 @@ def get_context(context): index_ = get_lesson_index(context.course, context.batch, frappe.session.user) or "1.1" else: index_ = "1.1" - frappe.local.flags.redirect_location = context.course.get_learn_url(index_) + frappe.local.flags.redirect_location = context.course.get_learn_url(index_) + context.course.query_parameter raise frappe.Redirect context.lesson = context.course.get_lesson(chapter_index, lesson_index) @@ -30,8 +30,8 @@ def get_context(context): next_ = outline.get_next(lesson_number) context.prev_chap = get_chapter_title(course_name, prev_) context.next_chap = get_chapter_title(course_name, next_) - context.next_url = context.course.get_learn_url(next_) - context.prev_url = context.course.get_learn_url(prev_) + context.next_url = context.course.get_learn_url(next_) + context.course.query_parameter + context.prev_url = context.course.get_learn_url(prev_) + context.course.query_parameter context.page_extensions = get_page_extensions() diff --git a/community/www/batch/members.html b/community/www/batch/members.html index 32baa20d..82448d6d 100644 --- a/community/www/batch/members.html +++ b/community/www/batch/members.html @@ -10,7 +10,7 @@ {% block content %}
    - {{ widgets.BatchTabs(course=course, batch=batch) }} + {{ widgets.BatchTabs(course=course, membership=membership) }} {{ MembersList(members)}}
    {% endblock %} diff --git a/community/www/batch/members.py b/community/www/batch/members.py index bb2a53a2..937af7ea 100644 --- a/community/www/batch/members.py +++ b/community/www/batch/members.py @@ -3,5 +3,5 @@ from . import utils def get_context(context): utils.get_common_context(context) - if not context.batch: + if not context.membership: utils.redirect_to_lesson(context.course) diff --git a/community/www/batch/progress.html b/community/www/batch/progress.html index 0762d835..3fa41bfe 100644 --- a/community/www/batch/progress.html +++ b/community/www/batch/progress.html @@ -24,7 +24,7 @@ {% block content %}
    - {{ widgets.BatchTabs(course=course, batch=batch) }} + {{ widgets.BatchTabs(course=course, membership=membership) }}

    Batch Progress

    {% for exercise in report.exercises %} diff --git a/community/www/batch/progress.py b/community/www/batch/progress.py index 70024864..446714e9 100644 --- a/community/www/batch/progress.py +++ b/community/www/batch/progress.py @@ -17,7 +17,7 @@ def get_context(context): class BatchReport: def __init__(self, course, batch): - self.submissions = get_submissions(batch) + self.submissions = get_submissions(course, batch) self.exercises = self.get_exercises(course.name) self.submissions_by_exercise = defaultdict(list) for s in self.submissions: @@ -29,8 +29,10 @@ class BatchReport: def get_submissions_of_exercise(self, exercise_name): return self.submissions_by_exercise[exercise_name] -def get_submissions(batch): - students = batch.get_students() +def get_submissions(course, batch): + students = course.get_students(batch.name) + if not len(students): + return [] students_map = {s.email: s for s in students} names, values = nparams("s", students_map.keys()) sql = """ diff --git a/community/www/batch/utils.py b/community/www/batch/utils.py index cd7e6b50..23060680 100644 --- a/community/www/batch/utils.py +++ b/community/www/batch/utils.py @@ -5,26 +5,34 @@ def get_common_context(context): context.no_cache = 1 course_name = frappe.form_dict["course"] + try: + batch_name = frappe.form_dict["batch"] + except KeyError: + batch_name = None course = Course.find(course_name) if not course: context.template = "www/404.html" return + context.course = course - batch_name = course.get_current_batch(frappe.session.user) - batch = course.get_batch(batch_name) - context.batch = batch - if batch_name: - context.members = batch.get_mentors() + batch.get_students() + membership = course.get_membership(frappe.session.user, batch_name) + if membership: + context.membership = membership + batch = course.get_batch(membership.batch) + + if batch: + context.batch = batch + + context.members = course.get_mentors(membership.batch) + course.get_students(membership.batch) context.member_count = len(context.members) - - context.course = course + context.course.query_parameter = "?batch=" + membership.batch if membership and membership.batch else "" context.livecode_url = get_livecode_url() def get_livecode_url(): return frappe.db.get_single_value("LMS Settings", "livecode_url") def redirect_to_lesson(course, index_="1.1"): - frappe.local.flags.redirect_location = course.get_learn_url(index_) + frappe.local.flags.redirect_location = course.get_learn_url(index_) + course.query_parameter raise frappe.Redirect diff --git a/community/www/courses/course.html b/community/www/courses/course.html index d18dc765..7de4c538 100644 --- a/community/www/courses/course.html +++ b/community/www/courses/course.html @@ -12,7 +12,14 @@
    Courses / {{ course.title }}
    -

    {{course.title}}

    +
    +

    {{course.title}}

    + {% if not course.disable_self_learning and not course.is_mentor(frappe.session.user) %} +
    + +
    + {% endif %} +
    {{ course.short_introduction }}
    @@ -96,8 +103,8 @@
    {% endfor %}
    -{% else %} -
    There are no Upcoming Batches for this course currently.
    -{% endif %} + {% else %} +
    There are no Upcoming Batches for this course currently.
    + {% endif %}
    {% endmacro %} diff --git a/community/www/courses/course.js b/community/www/courses/course.js index 142318e3..f2cad0a2 100644 --- a/community/www/courses/course.js +++ b/community/www/courses/course.js @@ -57,11 +57,13 @@ frappe.ready(() => { window.location.href = `/login?redirect-to=/courses/${course}`; return; } - batch = decodeURIComponent($(e.currentTarget).attr("data-batch")) + var batch = $(e.currentTarget).attr("data-batch"); + batch = batch ? decodeURIComponent(batch) : ""; frappe.call({ "method": "community.lms.doctype.lms_batch_membership.lms_batch_membership.create_membership", "args": { - "batch": batch + "batch": batch ? batch : "", + "course": course }, "callback": (data) => { if (data.message == "OK") { @@ -73,21 +75,4 @@ frappe.ready(() => { } }) }) - - $(".manage-batch").click((e) => { - e.preventDefault(); - var batch = decodeURIComponent($(e.currentTarget).attr("data-batch")); - var course = decodeURIComponent($(e.currentTarget).attr("data-course")); - frappe.call({ - method: "community.lms.doctype.lms_batch_membership.lms_batch_membership.update_current_membership", - args: { - batch: batch, - course: course, - member: frappe.session.user - }, - callback: (data) => { - window.location.href = `/courses/${course}/home`; - } - }) - }) }) diff --git a/community/www/courses/course.py b/community/www/courses/course.py index e2ff459f..3bc2f50f 100644 --- a/community/www/courses/course.py +++ b/community/www/courses/course.py @@ -16,9 +16,8 @@ def get_context(context): raise frappe.Redirect context.course = course - - batch = course.get_student_batch(frappe.session.user) - if batch: - frappe.local.flags.redirect_location = f"/courses/{course.name}/learn" - raise frappe.Redirect - + if not course.is_mentor(frappe.session.user): + batch = course.get_membership(frappe.session.user) + if batch: + frappe.local.flags.redirect_location = f"/courses/{course.name}/learn" + raise frappe.Redirect