diff --git a/cypress/e2e/course_creation.cy.js b/cypress/e2e/course_creation.cy.js index 87caa7a7..a76bdf8b 100644 --- a/cypress/e2e/course_creation.cy.js +++ b/cypress/e2e/course_creation.cy.js @@ -5,39 +5,50 @@ describe("Course Creation", () => { // Create a course cy.get("a.btn").contains("Create a Course").click(); cy.wait(1000); - cy.url().should("include", "/courses/new-course"); - cy.button("Add Tag").click(); - cy.get(".course-card-pills").type("Test"); + cy.url().should("include", "/courses/new-course/edit"); cy.get("#title").type("Test Course"); cy.get("#intro").type("Test Course Short Introduction"); - cy.get("#video-link").type("-LPmw2Znl2c"); - cy.get("#published").check(); cy.get("#description").type("Test Course Description"); + cy.get("#video-link").type("-LPmw2Znl2c"); + cy.get("#tags-input").type("Test"); + cy.get("#published").check(); cy.wait(1000); - cy.button("Save Course Details").click(); + cy.button("Save").click(); // Add Chapter - cy.wait(3000); - cy.button("New Chapter").click(); - cy.get(".new-chapter .chapter-title-main").type("Test Chapter"); - cy.get(".new-chapter .chapter-description").type( - "Test Chapter Description" - ); - cy.get(".new-chapter .btn-save-chapter").click(); + cy.wait(1000); + cy.link("Course Outline").click(); + + cy.wait(1000); + cy.get(".edit-header .btn-add-chapter").click(); + cy.get("#chapter-title").type("Test Chapter"); + cy.get("#chapter-description").type("Test Chapter Description"); + cy.button("Save").click(); // Add Lesson - cy.wait(3000); - cy.get(".chapter-parent .btn-lesson").click(); - - cy.wait(3000); - cy.get("#title").type("Test Lesson"); - cy.get("#youtube").type("GoDtyItReto"); - cy.get("#body").type("Test Lesson Content"); cy.wait(1000); - cy.get(".btn-lesson").click(); + cy.link("Add Lesson").click(); + cy.wait(1000); + cy.get("#lesson-title").type("Test Lesson"); + + // Content + cy.get(".ce-block").click().type("{enter}"); + cy.get(".ce-toolbar__plus").click(); + cy.get('[data-item-name="youtube"]').click(); + cy.get('input[data-fieldname="youtube"]').type("GoDtyItReto"); + cy.button("Insert").click(); + cy.wait(1000); + + cy.get(".ce-block:last").click().type("{enter}"); + cy.get(".ce-block:last") + .click() + .type( + "This is an extremely big paragraph that is meant to test the UI. This is a very long paragraph. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now." + ); + cy.button("Save").click(); // View Course - cy.wait(3000); + cy.wait(1000); cy.visit("/courses"); cy.get(".course-card-title:first").contains("Test Course"); cy.get(".course-card:first").click(); @@ -59,7 +70,7 @@ describe("Course Creation", () => { cy.get(".lesson-info:first").click(); // View Lesson - cy.wait(3000); + cy.wait(1000); cy.url().should("include", "learn/1.1"); cy.get("#title").contains("Test Lesson"); cy.get(".lesson-video iframe").should( @@ -67,7 +78,9 @@ describe("Course Creation", () => { "src", "https://www.youtube.com/embed/GoDtyItReto" ); - cy.get(".lesson-content-card").contains("Test Lesson Content"); + cy.get(".lesson-content-card").contains( + "This is an extremely big paragraph that is meant to test the UI. This is a very long paragraph. It contains more than once sentence. Its meant to be this long as this is a UI test. Its unbearably long and I'm not sure why I'm typing this much. I'm just going to keep typing until I feel like its long enough. I think its long enough now. I'm going to stop typing now." + ); // Add Discussion cy.get(".reply").click(); @@ -79,7 +92,7 @@ describe("Course Creation", () => { cy.get(".submit-discussion").click(); // View Discussion - cy.wait(3000); + cy.wait(1000); cy.get(".discussion-topic-title:first").contains("Question Title"); cy.get(".sidebar-parent:first").click(); cy.get(".reply-text").contains( diff --git a/lms/hooks.py b/lms/hooks.py index c9078ef3..2a6e7f15 100644 --- a/lms/hooks.py +++ b/lms/hooks.py @@ -138,12 +138,18 @@ fixtures = ["Custom Field", "Function", "Industry"] website_route_rules = [ {"from_route": "/sketches/", "to_route": "sketches/sketch"}, {"from_route": "/courses/", "to_route": "courses/course"}, + {"from_route": "/courses//edit", "to_route": "courses/create"}, + {"from_route": "/courses//outline", "to_route": "courses/outline"}, {"from_route": "/courses//", "to_route": "courses/certificate"}, {"from_route": "/courses//learn", "to_route": "batch/learn"}, { "from_route": "/courses//learn/.", "to_route": "batch/learn", }, + { + "from_route": "/courses//learn/./edit", + "to_route": "batch/edit", + }, {"from_route": "/quizzes", "to_route": "batch/quiz_list"}, {"from_route": "/quizzes/", "to_route": "batch/quiz"}, {"from_route": "/classes/", "to_route": "classes/class"}, diff --git a/lms/lms/doctype/lms_course/lms_course.json b/lms/lms/doctype/lms_course/lms_course.json index beac3d4e..11e45195 100644 --- a/lms/lms/doctype/lms_course/lms_course.json +++ b/lms/lms/doctype/lms_course/lms_course.json @@ -57,7 +57,7 @@ }, { "fieldname": "description", - "fieldtype": "Markdown Editor", + "fieldtype": "Text Editor", "label": "Description", "reqd": 1 }, @@ -260,7 +260,7 @@ } ], "make_attachments_public": 1, - "modified": "2023-05-02 09:45:54.826328", + "modified": "2023-05-11 17:08:19.763405", "modified_by": "Administrator", "module": "LMS", "name": "LMS Course", diff --git a/lms/lms/doctype/lms_course/lms_course.py b/lms/lms/doctype/lms_course/lms_course.py index 21bf0093..82075328 100644 --- a/lms/lms/doctype/lms_course/lms_course.py +++ b/lms/lms/doctype/lms_course/lms_course.py @@ -2,7 +2,7 @@ # For license information, please see license.txt import json - +import random import frappe from frappe.model.document import Document from frappe.utils import cint @@ -72,7 +72,10 @@ class LMSCourse(Document): def autoname(self): if not self.name: - self.name = generate_slug(self.title, "LMS Course") + title = self.title + if self.title == "New Course": + title = self.title + str(random.randint(0, 99)) + self.name = generate_slug(title, "LMS Course") def __repr__(self): return f"" diff --git a/lms/lms/doctype/lms_quiz/lms_quiz.py b/lms/lms/doctype/lms_quiz/lms_quiz.py index f9b4326a..fa49b0a9 100644 --- a/lms/lms/doctype/lms_quiz/lms_quiz.py +++ b/lms/lms/doctype/lms_quiz/lms_quiz.py @@ -192,3 +192,10 @@ def check_input_answers(question, answer): return 1 return 0 + + +@frappe.whitelist() +def get_user_quizzes(): + return frappe.get_all( + "LMS Quiz", filters={"owner": frappe.session.user}, fields=["name", "title"] + ) diff --git a/lms/lms/utils.py b/lms/lms/utils.py index 3a22fbdf..e8c2554d 100644 --- a/lms/lms/utils.py +++ b/lms/lms/utils.py @@ -93,18 +93,24 @@ def get_chapters(course): return chapters -def get_lessons(course, chapter=None): +def get_lessons(course, chapter=None, get_details=True): """If chapter is passed, returns lessons of only that chapter. Else returns lessons of all chapters of the course""" lessons = [] + lesson_count = 0 if chapter: - return get_lesson_details(chapter) + if get_details: + return get_lesson_details(chapter) + else: + return frappe.db.count("Lesson Reference", {"parent": chapter.name}) for chapter in get_chapters(course): - lesson = get_lesson_details(chapter) - lessons += lesson + if get_details: + lessons += get_lesson_details(chapter) + else: + lesson_count += frappe.db.count("Lesson Reference", {"parent": chapter.name}) - return lessons + return lessons if get_details else lesson_count def get_lesson_details(chapter): @@ -135,8 +141,8 @@ def get_lesson_details(chapter): macros = find_macros(lesson_details.body) for macro in macros: - if macro[0] == "YouTubeVideo": - lesson_details.icon = "icon-video" + if macro[0] == "YouTubeVideo" or macro[0] == "Video": + lesson_details.icon = "icon-youtube" elif macro[0] == "Quiz": lesson_details.icon = "icon-quiz" lessons.append(lesson_details) @@ -495,12 +501,17 @@ def can_create_courses(member=None): if not member: member = frappe.session.user + if frappe.session.user == "Guest": + return False + + if has_course_instructor_role(member) or has_course_moderator_role(member): + return True + portal_course_creation = frappe.db.get_single_value( "LMS Settings", "portal_course_creation" ) - return frappe.session.user != "Guest" and ( - portal_course_creation == "Anyone" or has_course_instructor_role(member) - ) + + return portal_course_creation == "Anyone" def has_course_moderator_role(member=None): @@ -611,15 +622,17 @@ def get_filtered_membership(course, memberships): def show_start_learing_cta(course, membership): - return ( - not course.disable_self_learning - and not membership - and not course.upcoming - and not check_profile_restriction() - and not is_instructor(course.name) - and course.status == "Approved" - and has_lessons(course) - ) + + if course.disable_self_learning or course.upcoming: + return False + if is_instructor(course.name): + return False + if course.status != "Approved": + return False + if not has_lessons(course): + return False + if not membership: + return True def has_lessons(course): diff --git a/lms/lms/widgets/CourseOutline.html b/lms/lms/widgets/CourseOutline.html index 5a765218..a79e4bef 100644 --- a/lms/lms/widgets/CourseOutline.html +++ b/lms/lms/widgets/CourseOutline.html @@ -1,81 +1,66 @@ {% set chapters = get_chapters(course.name) %} +{% set is_instructor = is_instructor(course.name) %} -{% if course.edit_mode or chapters | length %} +{% if chapters | length %}
- - {% if course.edit_mode and course.name %} - - {% endif %} - - {% if course.name and (course.edit_mode or chapters | length) %} -
+ {% if not lesson_page %} +
{{ _("Course Content") }}
- {% endif %} - {% if course.edit_mode and course.name and not chapters | length %} -
-
-
- -
+ {% endif %} {% if chapters | length %} -
+
{% for chapter in chapters %} -
-