blob: 7b972fbfe09e23a21173032936fee6c1bc109563 [file] [log] [blame]
scmmishra35bf5612018-11-13 16:36:22 +05301from __future__ import unicode_literals
scmmishrababb68d2018-11-19 16:13:21 +05302import erpnext.education.utils as utils
scmmishra35bf5612018-11-13 16:36:22 +05303import frappe
4
5# Academy Utils
6@frappe.whitelist(allow_guest=True)
7def get_portal_details():
scmmishrababb68d2018-11-19 16:13:21 +05308 """
9 Returns portal details from Education Settings Doctype. This contains the Title and Description for LMS amoung other things.
10 """
scmmishra35bf5612018-11-13 16:36:22 +053011 settings = frappe.get_doc("Education Settings")
12 title = settings.portal_title
13 description = settings.description
14 return dict(title=title, description=description)
15
scmmishra35bf5612018-11-13 16:36:22 +053016@frappe.whitelist(allow_guest=True)
17def get_featured_programs():
18 featured_program_names = frappe.get_all("Program", filters={"is_published": True, "is_featured": True})
19 if featured_program_names:
scmmishrababb68d2018-11-19 16:13:21 +053020 featured_list = [utils.get_program(program['name']) for program in featured_program_names]
scmmishra35bf5612018-11-13 16:36:22 +053021 return featured_list
22 else:
23 return None
24
scmmishra85c2fee2018-11-14 14:23:06 +053025@frappe.whitelist(allow_guest=True)
26def get_all_programs():
27 program_names = frappe.get_all("Program", filters={"is_published": True})
28 if program_names:
scmmishrababb68d2018-11-19 16:13:21 +053029 featured_list = [utils.get_program(program['name']) for program in program_names]
scmmishra85c2fee2018-11-14 14:23:06 +053030 return featured_list
31 else:
32 return None
33
scmmishra35bf5612018-11-13 16:36:22 +053034@frappe.whitelist(allow_guest=True)
35def get_program_details(program_name):
36 try:
37 program = frappe.get_doc('Program', program_name)
38 return program
39 except:
40 return None
41
scmmishra35bf5612018-11-13 16:36:22 +053042# Functions to get program & course details
43@frappe.whitelist(allow_guest=True)
44def get_courses(program_name):
45 program = frappe.get_doc('Program', program_name)
46 courses = program.get_course_list()
47 course_data = [{'meta':get_continue_content(item.name), 'course':item} for item in courses]
48 return course_data
49
scmmishra35bf5612018-11-13 16:36:22 +053050def get_continue_content(course_name):
51 if frappe.session.user == "Guest":
52 return dict(content=None, content_type=None, flag=None)
scmmishrababb68d2018-11-19 16:13:21 +053053 enrollment = utils.get_course_enrollment(course_name)
scmmishra35bf5612018-11-13 16:36:22 +053054 course = frappe.get_doc("Course", enrollment.course)
55 last_activity = enrollment.get_last_activity()
56
57 if last_activity == None:
58 next_content = course.get_first_content()
59 return dict(content=next_content.name, content_type=next_content.doctype, flag="Start")
60
61 if last_activity.doctype == "Quiz Activity":
62 next_content = get_next_content(last_activity.quiz, "Quiz", course.name)
63 else:
64 next_content = get_next_content(last_activity.content, last_activity.content_type, course.name)
65
66 if next_content == None:
67 next_content = course.get_first_content()
68 return dict(content=next_content.name, content_type=next_content.doctype, flag="Complete")
69 else:
70 next_content['flag'] = "Continue"
71 return next_content
72
scmmishra35bf5612018-11-13 16:36:22 +053073def get_starting_content(course_name):
74 course = frappe.get_doc('Course', course_name)
75 content = course.course_content[0].content
76 content_type = course.course_content[0].content_type
77 return dict(content=content, content_type=content_type)
78
79# Functions to get content details
80@frappe.whitelist()
81def get_content(content_name, content_type):
82 try:
83 content = frappe.get_doc(content_type, content_name)
84 return content
85 except:
86 frappe.throw("{0} with name {1} does not exist".format(content_type, content_name))
87 return None
88
89@frappe.whitelist()
90def get_next_content(content, content_type, course):
91 if frappe.session.user == "Guest":
92 return None
93 course_doc = frappe.get_doc("Course", course)
94 content_list = [{'content_type':item.content_type, 'content':item.content} for item in course_doc.get_all_children()]
95 current_index = content_list.index({'content': content, 'content_type': content_type})
96 try:
97 return content_list[current_index + 1]
98 except IndexError:
99 return None
100
101def get_quiz_with_answers(quiz_name):
102 try:
103 quiz = frappe.get_doc("Quiz", quiz_name).get_questions()
104 quiz_output = [{'name':question.name, 'question':question.question, 'options':[{'name': option.name, 'option':option.option, 'is_correct':option.is_correct} for option in question.options]} for question in quiz]
105 return quiz_output
106 except:
107 frappe.throw("Quiz {0} does not exist".format(quiz_name))
108 return None
109
110@frappe.whitelist()
111def get_quiz_without_answers(quiz_name):
112 try:
113 quiz = frappe.get_doc("Quiz", quiz_name).get_questions()
114 quiz_output = [{'name':question.name, 'question':question.question, 'options':[{'name': option.name, 'option':option.option} for option in question.options]} for question in quiz]
115 return quiz_output
116 except:
117 frappe.throw("Quiz {0} does not exist".format(quiz_name))
118 return None
119
120@frappe.whitelist()
121def evaluate_quiz(enrollment, quiz_response, quiz_name):
122 """LMS Function: Evaluates a simple multiple choice quiz.
123
124
125 :param quiz_response: contains user selected choices for a quiz in the form of a string formatted as a dictionary. The function uses `json.loads()` to convert it to a python dictionary.
126 """
scmmishrababb68d2018-11-19 16:13:21 +0530127
scmmishra35bf5612018-11-13 16:36:22 +0530128 import json
129 quiz_response = json.loads(quiz_response)
130 quiz = frappe.get_doc("Quiz", quiz_name)
131 answers, score, status = quiz.evaluate(quiz_response, quiz_name)
132
133 result = {k: ('Correct' if v else 'Wrong') for k,v in answers.items()}
134 result_data = []
135 for key in answers:
136 item = {}
137 item['question'] = key
138 item['quiz_result'] = result[key]
139 try:
140 item['selected_option'] = frappe.get_value('Options', quiz_response[key], 'option')
141 except:
142 item['selected_option'] = "Unattempted"
143 result_data.append(item)
144 # result_data = [{'question': key, 'selected_option': frappe.get_value('Options', quiz_response[key], 'option'), 'quiz_result': result[key]} for key in answers]
145
146 add_quiz_activity(enrollment, quiz_name, result_data, score, status)
147 return(score)
148
scmmishrababb68d2018-11-19 16:13:21 +0530149def add_quiz_activity(enrollment, quiz_name, result_data, score, status):
150 quiz_activity = frappe.get_doc({
151 "doctype": "Quiz Activity",
152 "enrollment": enrollment,
153 "quiz": quiz_name,
154 "activity_date": frappe.utils.datetime.datetime.now(),
155 "result": result_data,
156 "score": score,
157 "status": status
158 })
159 quiz_activity.save()
160 frappe.db.commit()
scmmishra35bf5612018-11-13 16:36:22 +0530161
162@frappe.whitelist()
163def get_continue_data(program_name):
164 program = frappe.get_doc("Program", program_name)
165 courses = program.get_all_children()
166 try:
167 continue_data = get_starting_content(courses[0].course)
168 continue_data['course'] = courses[0].course
169 return continue_data
170 except:
171 return None
172
173@frappe.whitelist()
scmmishra35bf5612018-11-13 16:36:22 +0530174def enroll_in_program(program_name):
scmmishrababb68d2018-11-19 16:13:21 +0530175 if(not utils.get_current_student()):
176 utils.create_student(frappe.session.user)
177 student = frappe.get_doc("Student", utils.get_current_student())
scmmishra35bf5612018-11-13 16:36:22 +0530178 program_enrollment = student.enroll_in_program(program_name)
scmmishrababb68d2018-11-19 16:13:21 +0530179 utils.enroll_all_courses_in_program(program_enrollment, student)
scmmishra0a4902f2018-11-15 11:16:53 +0530180 return program_name
scmmishra35bf5612018-11-13 16:36:22 +0530181
182@frappe.whitelist()
scmmishrababb68d2018-11-19 16:13:21 +0530183def get_program_enrollments():
184 if utils.get_current_student() == None:
scmmishra35bf5612018-11-13 16:36:22 +0530185 return None
186 try:
scmmishrababb68d2018-11-19 16:13:21 +0530187 student = frappe.get_doc("Student", utils.get_current_student())
scmmishra35bf5612018-11-13 16:36:22 +0530188 return student.get_program_enrollments()
189 except:
190 return None
191
192@frappe.whitelist()
scmmishrababb68d2018-11-19 16:13:21 +0530193def get_all_course_enrollments():
194 student = utils.get_current_student()
scmmishra35bf5612018-11-13 16:36:22 +0530195 if student == None:
196 return None
197 try:
198 student = frappe.get_doc("Student", student)
scmmishrababb68d2018-11-19 16:13:21 +0530199 return student.get_all_course_enrollments()
scmmishra35bf5612018-11-13 16:36:22 +0530200 except:
201 return None
202
scmmishra35bf5612018-11-13 16:36:22 +0530203# Academty Activity
204@frappe.whitelist()
205def add_activity(enrollment, content_type, content):
scmmishrababb68d2018-11-19 16:13:21 +0530206 if(utils.check_activity_exists(enrollment, content_type, content)):
scmmishra35bf5612018-11-13 16:36:22 +0530207 pass
208 else:
209 activity = frappe.get_doc({
210 "doctype": "Course Activity",
211 "enrollment": enrollment,
212 "content_type": content_type,
213 "content": content,
214 "activity_date": frappe.utils.datetime.datetime.now()
215 })
216 activity.save()
217 frappe.db.commit()
218
scmmishrababb68d2018-11-19 16:13:21 +0530219def get_course_progress(course_enrollment):
220 course = frappe.get_doc('Course', course_enrollment.course)
scmmishra35bf5612018-11-13 16:36:22 +0530221
scmmishrababb68d2018-11-19 16:13:21 +0530222 content_activity, quiz_activity = course_enrollment.get_linked_activity()
223 content_list, quiz_list = course.get_contents_based_on_type()
224
225 quiz_scores, is_quiz_complete, last_quiz_attempted = get_quiz_progress(quiz_list, quiz_activity)
226 is_content_complete, last_content_viewed = get_content_progress(content_list, content_activity)
scmmishra35bf5612018-11-13 16:36:22 +0530227
scmmishrababb68d2018-11-19 16:13:21 +0530228 quiz_data = {
229 'gradable_quiz_attempts': quiz_scores,
230 'complete': is_quiz_complete,
231 'last': last_quiz_attempted
232 }
233
234 content_data = {
235 'complete': is_content_complete,
236 'last': last_content_viewed
237 }
238
239 return quiz_data, content_data
240
241def get_quiz_progress(quiz_list, quiz_activity):
242 scores = []
243 is_complete = True
244 last_attempted = None
245 for quiz in quiz_list:
246 attempts = [attempt for attempt in quiz_activity if attempt.quiz==quiz.name]
247 if attempts and quiz.grading_basis == 'Last Attempt':
248 scores.append(attempts[0])
249 last_attempted = quiz
250 elif attempts and quiz.grading_basis == 'Last Highest Score':
251 sorted_by_score = sorted(attempts, key = lambda i: int(i.score), reverse=True)
scmmishrababb68d2018-11-19 16:13:21 +0530252 scores.append(sorted_by_score[0])
253 last_attempted = quiz
254 elif not attempts:
255 is_complete = False
256 return scores, is_complete, last_attempted
257
258def get_content_progress(content_list, content_activity):
259 is_complete = True
260 last_viewed = None
261 activity_list = [[activity.content, activity.content_type] for activity in content_activity]
262 for item in content_list:
263 current_content = [item.name, item.doctype]
264 if current_content in activity_list:
265 last_viewed = item
266 else:
267 is_complete = False
268 return is_complete, last_viewed