Compare commits

..

5 Commits

Author SHA1 Message Date
Anand Chitipothu
c96a14c972 feat: ignore orphan exercises in the progress
Don't show exercises that are not added to any lesson in the progress.
2021-06-01 08:15:52 +05:30
Anand Chitipothu
400e706be1 feat: update the index of orphan exercises
When an exercise is removed from a lesson, the link to the lesson is
removed from that exercise and the index is reset. This will make sure
the removed exercises won't show up in places like progress.
2021-06-01 05:59:01 +05:30
Anand Chitipothu
a12a52747e feat: show exercise index in the title
Show exercise as "Exercise 2.1: Draw a Circle".
2021-06-01 05:49:45 +05:30
Anand Chitipothu
b9a93bb160 feat: added actions to reindex lessons and exercises
Some lessons gets deleted and some new ones get added in the progress of
course creation and it may happen then some of the lesson index may
become inconsistent.  Also, we would like to maintain an index for the
exercises. To support both of these, added actions to reindex lessons
and exercises to the course doctype.
2021-06-01 05:46:32 +05:30
Jannat Patel
9c65ff8ae6 Merge pull request #113 from fossunited/invite-based-membership
feat: Invite based membership
2021-05-31 13:41:12 +05:30
10 changed files with 114 additions and 14 deletions

View File

@@ -10,6 +10,6 @@ class Chapter(Document):
def get_lessons(self):
rows = frappe.db.get_all("Lesson",
filters={"chapter": self.name},
fields='*',
fields='name',
order_by="index_")
return [frappe.get_doc(dict(row, doctype='Lesson')) for row in rows]
return [frappe.get_doc('Lesson', row['name']) for row in rows]

View File

@@ -15,7 +15,9 @@
"hints",
"tests",
"image",
"lesson"
"lesson",
"index_",
"index_label"
],
"fields": [
{
@@ -27,6 +29,7 @@
{
"fieldname": "course",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Course",
"options": "LMS Course"
},
@@ -73,13 +76,27 @@
{
"fieldname": "lesson",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Lesson",
"options": "Lesson"
},
{
"fieldname": "index_",
"fieldtype": "Int",
"label": "Index",
"read_only": 1
},
{
"fieldname": "index_label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Index Label",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-05-20 13:23:12.340928",
"modified": "2021-06-01 05:22:15.656013",
"modified_by": "Administrator",
"module": "LMS",
"name": "Exercise",
@@ -99,8 +116,8 @@
}
],
"search_fields": "title",
"sort_field": "modified",
"sort_order": "DESC",
"sort_field": "index_label",
"sort_order": "ASC",
"title_field": "title",
"track_changes": 1
}

View File

@@ -25,7 +25,6 @@ class Exercise(Document):
order_by="creation desc",
page_length=1)
print("get_user_submission", result)
if result:
return result[0]

View File

@@ -10,6 +10,7 @@
"lesson_type",
"title",
"index_",
"index_label",
"body",
"sections"
],
@@ -51,11 +52,18 @@
"fieldtype": "Table",
"label": "Sections",
"options": "LMS Section"
},
{
"fieldname": "index_label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Index Label",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-05-13 20:03:51.510605",
"modified": "2021-06-01 05:30:48.127494",
"modified_by": "Administrator",
"module": "LMS",
"name": "Lesson",

View File

@@ -16,10 +16,28 @@ class Lesson(Document):
e = s.get_exercise()
e.lesson = self.name
e.save()
self.update_orphan_exercises()
def update_orphan_exercises(self):
"""Updates the exercises that were previously part of this lesson,
but not any more.
"""
linked_exercises = {row['name'] for row in frappe.get_all('Exercise', {"lesson": self.name})}
active_exercises = {s.id for s in self.get("sections") if s.type=="exercise"}
orphan_exercises = linked_exercises - active_exercises
for name in orphan_exercises:
ex = frappe.get_doc("Exercise", name)
ex.lesson = None
ex.index_ = 0
ex.index_label = ""
ex.save()
def get_sections(self):
return sorted(self.get('sections'), key=lambda s: s.index)
def get_exercises(self):
return [frappe.get_doc("Exercise", s.id) for s in self.get("sections") if s.type=="exercise"]
def make_lms_section(self, index, section):
s = frappe.new_doc('LMS Section', parent_doc=self, parentfield='sections')
s.type = section.type

View File

@@ -1,5 +1,18 @@
{
"actions": [],
"actions": [
{
"action": "community.lms.doctype.lms_course.lms_course.reindex_lessons",
"action_type": "Server Action",
"group": "Reindex",
"label": "Reindex Lessons"
},
{
"action": "community.lms.doctype.lms_course.lms_course.reindex_exercises",
"action_type": "Server Action",
"group": "Reindex",
"label": "Reindex Exercises"
}
],
"allow_guest_to_view": 1,
"allow_rename": 1,
"creation": "2021-03-01 16:49:33.622422",
@@ -86,7 +99,7 @@
"link_fieldname": "course"
}
],
"modified": "2021-05-23 18:14:32.602647",
"modified": "2021-06-01 04:36:45.696776",
"modified_by": "Administrator",
"module": "LMS",
"name": "LMS Course",

View File

@@ -5,6 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
import json
from ...utils import slugify
from community.query import find, find_all
@@ -157,6 +158,35 @@ class LMSCourse(Document):
chapter = frappe.get_doc("Chapter", lesson.chapter)
return f"{chapter.index_}.{lesson.index_}"
def reindex_lessons(self):
for i, c in enumerate(self.get_chapters(), start=1):
c.index_ = i
c.save()
self._reindex_lessons_in_chapter(c)
def _reindex_lessons_in_chapter(self, c):
for i, lesson in enumerate(c.get_lessons(), start=1):
lesson.index = i
lesson.index_label = f"{c.index_}.{i}"
lesson.save()
def reindex_exercises(self):
for i, c in enumerate(self.get_chapters(), start=1):
if c.index_ != i:
c.index_ = i
c.save()
self._reindex_exercises_in_chapter(c)
def _reindex_exercises_in_chapter(self, c):
i = 1
for lesson in c.get_lessons():
for exercise in lesson.get_exercises():
exercise.index_ = i
exercise.index_label = f"{c.index_}.{i}"
exercise.save()
i += 1
def get_outline(self):
return CourseOutline(self)
@@ -187,7 +217,8 @@ class CourseOutline:
def get_chapters(self):
return frappe.db.get_all("Chapter",
filters={"course": self.course.name},
fields=["name", "title", "index_"])
fields=["name", "title", "index_"],
order_by="index_")
def get_lessons(self):
chapters = [c['name'] for c in self.chapters]
@@ -199,3 +230,17 @@ class CourseOutline:
for lesson in lessons:
lesson['number'] = "{}.{}".format(chapter_numbers[lesson['chapter']], lesson['index_'])
return lessons
@frappe.whitelist()
def reindex_lessons(doc):
course_data = json.loads(doc)
course = frappe.get_doc("LMS Course", course_data['name'])
course.reindex_lessons()
frappe.msgprint("All lessons in this course have been re-indexed.")
@frappe.whitelist()
def reindex_exercises(doc):
course_data = json.loads(doc)
course = frappe.get_doc("LMS Course", course_data['name'])
course.reindex_exercises()
frappe.msgprint("All exercises in this course have been re-indexed.")

View File

@@ -1,7 +1,7 @@
{% from "www/macros/livecode.html" import LiveCodeEditorJS, LiveCodeEditor with context %}
<div class="exercise">
<h2>{{ exercise.title }}</h2>
<h2>Exercise {{exercise.index_label}}: {{ exercise.title }}</h2>
<div class="exercise-description">{{frappe.utils.md_to_html(exercise.description)}}</div>
{% if exercise.image %}

View File

@@ -29,7 +29,7 @@
<h1>Batch Progress</h1>
{% for exercise in report.exercises %}
<div class="exercise-submissions">
<h2>{{exercise.title}}</h2>
<h2>Exercise {{exercise.index_label}}: {{exercise.title}}</h2>
{% for s in report.get_submissions_of_exercise(exercise.name) %}
<div class="submission">
<h4><a href="/{{s.owner.username}}">{{s.owner.full_name}}</a></h4>

View File

@@ -25,7 +25,7 @@ class BatchReport:
self.submissions_by_exercise[s.exercise].append(s)
def get_exercises(self, course_name):
return frappe.get_all("Exercise", {"course": course_name}, ["name", "title"])
return frappe.get_all("Exercise", {"course": course_name, "lesson": ["!=", ""]}, ["name", "title", "index_label"], order_by="index_label")
def get_submissions_of_exercise(self, exercise_name):
return self.submissions_by_exercise[exercise_name]