blob: e6eb1c9987440b63b402adb5290f7d11acb6b122 [file] [log] [blame]
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +05301# Copyright (c) 2015, Frappe Technologies and contributors
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +05302
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +05303import frappe
4from frappe import _
5
Chillar Anand915b3432021-09-02 16:44:59 +05306
Ankush Menat494bd9e2022-03-28 18:52:46 +05307class OverlapError(frappe.ValidationError):
8 pass
9
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +053010
11def validate_overlap_for(doc, doctype, fieldname, value=None):
Manas Solanki54c42402017-04-12 19:24:12 +053012 """Checks overlap for specified field.
scmmishra685584b2018-10-17 12:41:50 +053013
14 :param fieldname: Checks Overlap for this field
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +053015 """
scmmishra685584b2018-10-17 12:41:50 +053016
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +053017 existing = get_overlap_for(doc, doctype, fieldname, value)
18 if existing:
Ankush Menat494bd9e2022-03-28 18:52:46 +053019 frappe.throw(
20 _("This {0} conflicts with {1} for {2} {3}").format(
21 doc.doctype,
22 existing.name,
23 doc.meta.get_label(fieldname) if not value else fieldname,
24 value or doc.get(fieldname),
25 ),
26 OverlapError,
27 )
28
scmmishra685584b2018-10-17 12:41:50 +053029
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +053030def get_overlap_for(doc, doctype, fieldname, value=None):
Manas Solanki54c42402017-04-12 19:24:12 +053031 """Returns overlaping document for specified field.
scmmishra685584b2018-10-17 12:41:50 +053032
33 :param fieldname: Checks Overlap for this field
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +053034 """
35
Ankush Menat494bd9e2022-03-28 18:52:46 +053036 existing = frappe.db.sql(
37 """select name, from_time, to_time from `tab{0}`
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +053038 where `{1}`=%(val)s and schedule_date = %(schedule_date)s and
39 (
40 (from_time > %(from_time)s and from_time < %(to_time)s) or
41 (to_time > %(from_time)s and to_time < %(to_time)s) or
42 (%(from_time)s > from_time and %(from_time)s < to_time) or
43 (%(from_time)s = from_time and %(to_time)s = to_time))
Ankush Menat494bd9e2022-03-28 18:52:46 +053044 and name!=%(name)s and docstatus!=2""".format(
45 doctype, fieldname
46 ),
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +053047 {
48 "schedule_date": doc.schedule_date,
49 "val": value or doc.get(fieldname),
50 "from_time": doc.from_time,
51 "to_time": doc.to_time,
Ankush Menat494bd9e2022-03-28 18:52:46 +053052 "name": doc.name or "No Name",
53 },
54 as_dict=True,
55 )
Neil Trini Lasrado3f0a5812016-07-19 14:17:33 +053056
57 return existing[0] if existing else None
scmmishra685584b2018-10-17 12:41:50 +053058
scmmishra7409fe62018-10-22 18:37:03 +053059
Neil Trini Lasradof521a9c2016-07-22 01:28:41 +053060def validate_duplicate_student(students):
Ankush Menat494bd9e2022-03-28 18:52:46 +053061 unique_students = []
Neil Trini Lasradof521a9c2016-07-22 01:28:41 +053062 for stud in students:
63 if stud.student in unique_students:
Ankush Menat494bd9e2022-03-28 18:52:46 +053064 frappe.throw(
65 _("Student {0} - {1} appears Multiple times in row {2} & {3}").format(
66 stud.student, stud.student_name, unique_students.index(stud.student) + 1, stud.idx
67 )
68 )
Neil Trini Lasradof521a9c2016-07-22 01:28:41 +053069 else:
70 unique_students.append(stud.student)
scmmishra4ae11f42018-10-12 15:18:26 +053071
scmmishrababb68d2018-11-19 16:13:21 +053072 return None
73
Ankush Menat494bd9e2022-03-28 18:52:46 +053074
scmmishra11925292018-11-20 17:38:01 +053075# LMS Utils
scmmishrababb68d2018-11-19 16:13:21 +053076def get_current_student():
Shivam Mishraf9275022019-05-29 18:39:52 +053077 """Returns current student from frappe.session.user
78
79 Returns:
Ankush Menat494bd9e2022-03-28 18:52:46 +053080 object: Student Document
scmmishrababb68d2018-11-19 16:13:21 +053081 """
82 email = frappe.session.user
Ankush Menat494bd9e2022-03-28 18:52:46 +053083 if email in ("Administrator", "Guest"):
scmmishrababb68d2018-11-19 16:13:21 +053084 return None
85 try:
scmmishrada39da62018-12-13 11:51:31 +053086 student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name
scmmishra327334a2019-04-22 12:03:17 +053087 return frappe.get_doc("Student", student_id)
88 except (IndexError, frappe.DoesNotExistError):
scmmishra66d23922019-04-22 12:54:43 +053089 return None
scmmishrababb68d2018-11-19 16:13:21 +053090
Ankush Menat494bd9e2022-03-28 18:52:46 +053091
Shivam Mishraf9275022019-05-29 18:39:52 +053092def get_portal_programs():
93 """Returns a list of all program to be displayed on the portal
94 Programs are returned based on the following logic
Ankush Menat494bd9e2022-03-28 18:52:46 +053095 is_published and (student_is_enrolled or student_can_self_enroll)
scmmishra000e7062019-03-19 12:30:43 +053096
Shivam Mishraf9275022019-05-29 18:39:52 +053097 Returns:
Ankush Menat494bd9e2022-03-28 18:52:46 +053098 list of dictionary: List of all programs and to be displayed on the portal along with access rights
scmmishrababb68d2018-11-19 16:13:21 +053099 """
Shivam Mishraf9275022019-05-29 18:39:52 +0530100 published_programs = frappe.get_all("Program", filters={"is_published": True})
101 if not published_programs:
scmmishrababb68d2018-11-19 16:13:21 +0530102 return None
Shivam Mishraf9275022019-05-29 18:39:52 +0530103
104 program_list = [frappe.get_doc("Program", program) for program in published_programs]
Ankush Menat494bd9e2022-03-28 18:52:46 +0530105 portal_programs = [
106 {"program": program, "has_access": allowed_program_access(program.name)}
107 for program in program_list
108 if allowed_program_access(program.name) or program.allow_self_enroll
109 ]
Shivam Mishraf9275022019-05-29 18:39:52 +0530110
111 return portal_programs
112
Ankush Menat494bd9e2022-03-28 18:52:46 +0530113
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530114def allowed_program_access(program, student=None):
Shivam Mishraf9275022019-05-29 18:39:52 +0530115 """Returns enrollment status for current student
116
117 Args:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530118 program (string): Name of the program
119 student (object): instance of Student document
Shivam Mishraf9275022019-05-29 18:39:52 +0530120
121 Returns:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530122 bool: Is current user enrolled or not
Shivam Mishraf9275022019-05-29 18:39:52 +0530123 """
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530124 if has_super_access():
125 return True
Shivam Mishraf9275022019-05-29 18:39:52 +0530126 if not student:
127 student = get_current_student()
Ankush Menat494bd9e2022-03-28 18:52:46 +0530128 if student and get_enrollment("program", program, student.name):
Shivam Mishraf9275022019-05-29 18:39:52 +0530129 return True
scmmishrababb68d2018-11-19 16:13:21 +0530130 else:
Shivam Mishraf9275022019-05-29 18:39:52 +0530131 return False
scmmishrababb68d2018-11-19 16:13:21 +0530132
Ankush Menat494bd9e2022-03-28 18:52:46 +0530133
Shivam Mishraf9275022019-05-29 18:39:52 +0530134def get_enrollment(master, document, student):
135 """Gets enrollment for course or program
scmmishrababb68d2018-11-19 16:13:21 +0530136
Shivam Mishraf9275022019-05-29 18:39:52 +0530137 Args:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530138 master (string): can either be program or course
139 document (string): program or course name
140 student (string): Student ID
Shivam Mishraf9275022019-05-29 18:39:52 +0530141
142 Returns:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530143 string: Enrollment Name if exists else returns empty string
Shivam Mishraf9275022019-05-29 18:39:52 +0530144 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530145 if master == "program":
146 enrollments = frappe.get_all(
147 "Program Enrollment", filters={"student": student, "program": document, "docstatus": 1}
148 )
149 if master == "course":
150 enrollments = frappe.get_all(
151 "Course Enrollment", filters={"student": student, "course": document}
152 )
Shivam Mishraf9275022019-05-29 18:39:52 +0530153
154 if enrollments:
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530155 return enrollments[0].name
Shivam Mishraf9275022019-05-29 18:39:52 +0530156 else:
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530157 return None
Shivam Mishraf9275022019-05-29 18:39:52 +0530158
Ankush Menat494bd9e2022-03-28 18:52:46 +0530159
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530160@frappe.whitelist()
161def enroll_in_program(program_name, student=None):
162 """Enroll student in program
163
164 Args:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530165 program_name (string): Name of the program to be enrolled into
166 student (string, optional): name of student who has to be enrolled, if not
167 provided, a student will be created from the current user
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530168
169 Returns:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530170 string: name of the program enrollment document
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530171 """
172 if has_super_access():
173 return
174
175 if not student == None:
176 student = frappe.get_doc("Student", student)
177 else:
178 # Check if self enrollment in allowed
Ankush Menat494bd9e2022-03-28 18:52:46 +0530179 program = frappe.get_doc("Program", program_name)
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530180 if not program.allow_self_enroll:
Anurag Mishra841d8522019-07-03 15:15:08 +0530181 return frappe.throw(_("You are not allowed to enroll for this course"))
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530182
183 student = get_current_student()
184 if not student:
185 student = create_student_from_current_user()
186
187 # Check if student is already enrolled in program
Ankush Menat494bd9e2022-03-28 18:52:46 +0530188 enrollment = get_enrollment("program", program_name, student.name)
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530189 if enrollment:
190 return enrollment
191
192 # Check if self enrollment in allowed
Ankush Menat494bd9e2022-03-28 18:52:46 +0530193 program = frappe.get_doc("Program", program_name)
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530194 if not program.allow_self_enroll:
Anurag Mishra841d8522019-07-03 15:15:08 +0530195 return frappe.throw(_("You are not allowed to enroll for this course"))
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530196
197 # Enroll in program
198 program_enrollment = student.enroll_in_program(program_name)
199 return program_enrollment.name
200
Ankush Menat494bd9e2022-03-28 18:52:46 +0530201
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530202def has_super_access():
Shivam Mishrae94e9d22019-05-30 18:05:00 +0530203 """Check if user has a role that allows full access to LMS
204
205 Returns:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530206 bool: true if user has access to all lms content
Shivam Mishrae94e9d22019-05-30 18:05:00 +0530207 """
Ankush Menat494bd9e2022-03-28 18:52:46 +0530208 current_user = frappe.get_doc("User", frappe.session.user)
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530209 roles = set([role.role for role in current_user.roles])
Ankush Menat494bd9e2022-03-28 18:52:46 +0530210 return bool(
211 roles & {"Administrator", "Instructor", "Education Manager", "System Manager", "Academic User"}
212 )
213
Shivam Mishraf9275022019-05-29 18:39:52 +0530214
Shivam Mishrae94e9d22019-05-30 18:05:00 +0530215@frappe.whitelist()
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530216def add_activity(course, content_type, content, program):
Shivam Mishrae94e9d22019-05-30 18:05:00 +0530217 if has_super_access():
218 return None
Shivam Mishraf9275022019-05-29 18:39:52 +0530219
Shivam Mishrae94e9d22019-05-30 18:05:00 +0530220 student = get_current_student()
221 if not student:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530222 return frappe.throw(
223 _("Student with email {0} does not exist").format(frappe.session.user), frappe.DoesNotExistError
224 )
Shivam Mishraf9275022019-05-29 18:39:52 +0530225
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530226 enrollment = get_or_create_course_enrollment(course, program)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530227 if content_type == "Quiz":
Shivam Mishrae94e9d22019-05-30 18:05:00 +0530228 return
229 else:
230 return enrollment.add_activity(content_type, content)
scmmishrababb68d2018-11-19 16:13:21 +0530231
Ankush Menat494bd9e2022-03-28 18:52:46 +0530232
Shivam Mishrad1a25212019-06-03 12:57:38 +0530233@frappe.whitelist()
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530234def evaluate_quiz(quiz_response, quiz_name, course, program, time_taken):
Shivam Mishrad1a25212019-06-03 12:57:38 +0530235 import json
236
237 student = get_current_student()
238
239 quiz_response = json.loads(quiz_response)
240 quiz = frappe.get_doc("Quiz", quiz_name)
241 result, score, status = quiz.evaluate(quiz_response, quiz_name)
242
243 if has_super_access():
Ankush Menat494bd9e2022-03-28 18:52:46 +0530244 return {"result": result, "score": score, "status": status}
Shivam Mishrad1a25212019-06-03 12:57:38 +0530245
246 if student:
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530247 enrollment = get_or_create_course_enrollment(course, program)
248 if quiz.allowed_attempt(enrollment, quiz_name):
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530249 enrollment.add_quiz_activity(quiz_name, quiz_response, result, score, status, time_taken)
Ankush Menat494bd9e2022-03-28 18:52:46 +0530250 return {"result": result, "score": score, "status": status}
Shivam Mishrad1a25212019-06-03 12:57:38 +0530251 else:
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530252 return None
Shivam Mishrad1a25212019-06-03 12:57:38 +0530253
Ankush Menat494bd9e2022-03-28 18:52:46 +0530254
Shivam Mishrad1a25212019-06-03 12:57:38 +0530255@frappe.whitelist()
256def get_quiz(quiz_name, course):
257 try:
258 quiz = frappe.get_doc("Quiz", quiz_name)
259 questions = quiz.get_questions()
Ankush Menat694ae812021-09-01 14:40:56 +0530260 except Exception:
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530261 frappe.throw(_("Quiz {0} does not exist").format(quiz_name), frappe.DoesNotExistError)
Shivam Mishrad1a25212019-06-03 12:57:38 +0530262 return None
263
Ankush Menat494bd9e2022-03-28 18:52:46 +0530264 questions = [
265 {
266 "name": question.name,
267 "question": question.question,
268 "type": question.question_type,
269 "options": [{"name": option.name, "option": option.option} for option in question.options],
270 }
271 for question in questions
272 ]
Shivam Mishrad1a25212019-06-03 12:57:38 +0530273
274 if has_super_access():
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530275 return {
Ankush Menat494bd9e2022-03-28 18:52:46 +0530276 "questions": questions,
277 "activity": None,
278 "is_time_bound": quiz.is_time_bound,
279 "duration": quiz.duration,
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530280 }
Shivam Mishrad1a25212019-06-03 12:57:38 +0530281
282 student = get_current_student()
283 course_enrollment = get_enrollment("course", course, student.name)
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530284 status, score, result, time_taken = check_quiz_completion(quiz, course_enrollment)
285 return {
Ankush Menat494bd9e2022-03-28 18:52:46 +0530286 "questions": questions,
287 "activity": {"is_complete": status, "score": score, "result": result, "time_taken": time_taken},
288 "is_time_bound": quiz.is_time_bound,
289 "duration": quiz.duration,
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530290 }
Shivam Mishrad1a25212019-06-03 12:57:38 +0530291
Ankush Menat494bd9e2022-03-28 18:52:46 +0530292
Shivam Mishra16b41292019-06-05 17:29:48 +0530293def get_topic_progress(topic, course_name, program):
Shivam Mishra6d4c6662019-06-03 14:41:05 +0530294 """
295 Return the porgress of a course in a program as well as the content to continue from.
Ankush Menat494bd9e2022-03-28 18:52:46 +0530296 :param topic_name:
297 :param course_name:
Shivam Mishra6d4c6662019-06-03 14:41:05 +0530298 """
299 student = get_current_student()
Shivam Mishra65932632019-06-05 13:29:51 +0530300 if not student:
301 return None
Shivam Mishra6d4c6662019-06-03 14:41:05 +0530302 course_enrollment = get_or_create_course_enrollment(course_name, program)
303 progress = student.get_topic_progress(course_enrollment.name, topic)
304 if not progress:
Shivam Mishra570161b2019-06-05 13:08:53 +0530305 return None
Ankush Menat494bd9e2022-03-28 18:52:46 +0530306 count = sum([activity["is_complete"] for activity in progress])
Shivam Mishra6d4c6662019-06-03 14:41:05 +0530307 if count == 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530308 return {"completed": False, "started": False}
Shivam Mishra6d4c6662019-06-03 14:41:05 +0530309 elif count == len(progress):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530310 return {"completed": True, "started": True}
Shivam Mishra6d4c6662019-06-03 14:41:05 +0530311 elif count < len(progress):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530312 return {"completed": False, "started": True}
313
Shivam Mishra570161b2019-06-05 13:08:53 +0530314
Shivam Mishra16b41292019-06-05 17:29:48 +0530315def get_course_progress(course, program):
Shivam Mishra570161b2019-06-05 13:08:53 +0530316 """
317 Return the porgress of a course in a program as well as the content to continue from.
Ankush Menat494bd9e2022-03-28 18:52:46 +0530318 :param topic_name:
319 :param course_name:
Shivam Mishra570161b2019-06-05 13:08:53 +0530320 """
321 course_progress = []
322 for course_topic in course.topics:
323 topic = frappe.get_doc("Topic", course_topic.topic)
Shivam Mishra16b41292019-06-05 17:29:48 +0530324 progress = get_topic_progress(topic, course.name, program)
Shivam Mishra570161b2019-06-05 13:08:53 +0530325 if progress:
326 course_progress.append(progress)
Shivam Mishra570161b2019-06-05 13:08:53 +0530327 if course_progress:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530328 number_of_completed_topics = sum([activity["completed"] for activity in course_progress])
Shivam Mishra570161b2019-06-05 13:08:53 +0530329 total_topics = len(course_progress)
Shivam Mishra16b41292019-06-05 17:29:48 +0530330 if total_topics == 1:
331 return course_progress[0]
Shivam Mishra570161b2019-06-05 13:08:53 +0530332 if number_of_completed_topics == 0:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530333 return {"completed": False, "started": False}
Shivam Mishra570161b2019-06-05 13:08:53 +0530334 if number_of_completed_topics == total_topics:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530335 return {"completed": True, "started": True}
Shivam Mishra570161b2019-06-05 13:08:53 +0530336 if number_of_completed_topics < total_topics:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530337 return {"completed": False, "started": True}
Shivam Mishra570161b2019-06-05 13:08:53 +0530338
339 return None
Shivam Mishra6d4c6662019-06-03 14:41:05 +0530340
Ankush Menat494bd9e2022-03-28 18:52:46 +0530341
Shivam Mishra16b41292019-06-05 17:29:48 +0530342def get_program_progress(program):
343 program_progress = []
344 if not program.courses:
345 return None
346 for program_course in program.courses:
347 course = frappe.get_doc("Course", program_course.course)
348 progress = get_course_progress(course, program.name)
349 if progress:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530350 progress["name"] = course.name
351 progress["course"] = course.course_name
Shivam Mishra16b41292019-06-05 17:29:48 +0530352 program_progress.append(progress)
353
354 if program_progress:
355 return program_progress
356
357 return None
358
Ankush Menat494bd9e2022-03-28 18:52:46 +0530359
Shivam Mishra16b41292019-06-05 17:29:48 +0530360def get_program_completion(program):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530361 topics = frappe.db.sql(
362 """select `tabCourse Topic`.topic, `tabCourse Topic`.parent
Shivam Mishrad49b5e42019-06-06 17:19:53 +0530363 from `tabCourse Topic`,
364 `tabProgram Course`
365 where `tabCourse Topic`.parent = `tabProgram Course`.course
Ankush Menat494bd9e2022-03-28 18:52:46 +0530366 and `tabProgram Course`.parent = %s""",
367 program.name,
368 )
Shivam Mishra16b41292019-06-05 17:29:48 +0530369
370 progress = []
371 for topic in topics:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530372 topic_doc = frappe.get_doc("Topic", topic[0])
Shivam Mishra16b41292019-06-05 17:29:48 +0530373 topic_progress = get_topic_progress(topic_doc, topic[1], program.name)
374 if topic_progress:
375 progress.append(topic_progress)
376
377 if progress:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530378 number_of_completed_topics = sum([activity["completed"] for activity in progress if activity])
Shivam Mishra16b41292019-06-05 17:29:48 +0530379 total_topics = len(progress)
380 try:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530381 return int((float(number_of_completed_topics) / total_topics) * 100)
Shivam Mishra16b41292019-06-05 17:29:48 +0530382 except ZeroDivisionError:
383 return 0
384
385 return 0
386
Ankush Menat494bd9e2022-03-28 18:52:46 +0530387
scmmishrabf9a10f2019-03-06 15:45:35 +0530388def create_student_from_current_user():
scmmishra082e3c92018-11-23 17:16:22 +0530389 user = frappe.get_doc("User", frappe.session.user)
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530390
Ankush Menat494bd9e2022-03-28 18:52:46 +0530391 student = frappe.get_doc(
392 {
393 "doctype": "Student",
394 "first_name": user.first_name,
395 "last_name": user.last_name,
396 "student_email_id": user.email,
397 "user": frappe.session.user,
398 }
399 )
Shivam Mishradfdb92f2019-05-30 16:35:15 +0530400
scmmishrababb68d2018-11-19 16:13:21 +0530401 student.save(ignore_permissions=True)
scmmishra97c994f2018-11-26 14:41:15 +0530402 return student
403
Ankush Menat494bd9e2022-03-28 18:52:46 +0530404
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530405def get_or_create_course_enrollment(course, program):
scmmishra66d23922019-04-22 12:54:43 +0530406 student = get_current_student()
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530407 course_enrollment = get_enrollment("course", course, student.name)
408 if not course_enrollment:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530409 program_enrollment = get_enrollment("program", program.name, student.name)
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530410 if not program_enrollment:
Suraj Shetty48e9bc32020-01-29 15:06:18 +0530411 frappe.throw(_("You are not enrolled in program {0}").format(program))
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530412 return
Ankush Menat494bd9e2022-03-28 18:52:46 +0530413 return student.enroll_in_course(
414 course_name=course, program_enrollment=get_enrollment("program", program.name, student.name)
415 )
Shivam Mishra8ddb63a2019-06-03 14:40:52 +0530416 else:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530417 return frappe.get_doc("Course Enrollment", course_enrollment)
418
scmmishrababb68d2018-11-19 16:13:21 +0530419
scmmishra766f68a2018-12-12 16:14:36 +0530420def check_content_completion(content_name, content_type, enrollment_name):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530421 activity = frappe.get_all(
422 "Course Activity",
423 filters={"enrollment": enrollment_name, "content_type": content_type, "content": content_name},
424 )
scmmishra766f68a2018-12-12 16:14:36 +0530425 if activity:
426 return True
427 else:
428 return False
429
Ankush Menat494bd9e2022-03-28 18:52:46 +0530430
scmmishra766f68a2018-12-12 16:14:36 +0530431def check_quiz_completion(quiz, enrollment_name):
Ankush Menat494bd9e2022-03-28 18:52:46 +0530432 attempts = frappe.get_all(
433 "Quiz Activity",
434 filters={"enrollment": enrollment_name, "quiz": quiz.name},
435 fields=["name", "activity_date", "score", "status", "time_taken"],
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530436 )
Shivam Mishrad1a25212019-06-03 12:57:38 +0530437 status = False if quiz.max_attempts == 0 else bool(len(attempts) >= quiz.max_attempts)
scmmishra766f68a2018-12-12 16:14:36 +0530438 score = None
439 result = None
Jannat Pateldcdd3be2021-04-19 10:36:40 +0530440 time_taken = None
scmmishra766f68a2018-12-12 16:14:36 +0530441 if attempts:
Ankush Menat494bd9e2022-03-28 18:52:46 +0530442 if quiz.grading_basis == "Last Highest Score":
443 attempts = sorted(attempts, key=lambda i: int(i.score), reverse=True)
444 score = attempts[0]["score"]
445 result = attempts[0]["status"]
446 time_taken = attempts[0]["time_taken"]
447 if result == "Pass":
scmmishra766f68a2018-12-12 16:14:36 +0530448 status = True
pateljannatbbf07d92021-06-08 17:05:44 +0530449 return status, score, result, time_taken