feat: video watch time tracking
This commit is contained in:
@@ -1559,3 +1559,38 @@ def update_test_cases(test_cases, submission):
|
||||
}
|
||||
)
|
||||
test_case.insert()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def track_video_watch_duration(lesson, videos):
|
||||
"""
|
||||
Track the watch duration of videos in a lesson.
|
||||
"""
|
||||
if not isinstance(videos, list):
|
||||
videos = json.loads(videos)
|
||||
|
||||
for video in videos:
|
||||
filters = {
|
||||
"lesson": lesson,
|
||||
"source": video.get("source"),
|
||||
"member": frappe.session.user,
|
||||
}
|
||||
|
||||
if frappe.db.exists("LMS Video Watch Duration", filters):
|
||||
frappe.db.set_value(
|
||||
"LMS Video Watch Duration",
|
||||
filters,
|
||||
"watch_time",
|
||||
video.get("watch_time"),
|
||||
)
|
||||
else:
|
||||
track_new_watch_time(lesson, video)
|
||||
|
||||
|
||||
def track_new_watch_time(lesson, video):
|
||||
doc = frappe.new_doc("LMS Video Watch Duration")
|
||||
doc.lesson = lesson
|
||||
doc.source = video.get("source")
|
||||
doc.watch_time = video.get("watch_time")
|
||||
doc.member = frappe.session.user
|
||||
doc.save()
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2025, Frappe and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
// frappe.ui.form.on("LMS Video Watch Duration", {
|
||||
// refresh(frm) {
|
||||
|
||||
// },
|
||||
// });
|
||||
@@ -0,0 +1,160 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2025-06-30 13:00:22.655432",
|
||||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"lesson",
|
||||
"chapter",
|
||||
"course",
|
||||
"column_break_tmwj",
|
||||
"member",
|
||||
"member_name",
|
||||
"member_image",
|
||||
"member_username",
|
||||
"section_break_fywc",
|
||||
"source",
|
||||
"column_break_uuyv",
|
||||
"watch_time"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "lesson",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Lesson",
|
||||
"options": "Course Lesson",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "lesson.chapter",
|
||||
"fieldname": "chapter",
|
||||
"fieldtype": "Link",
|
||||
"label": "Chapter",
|
||||
"options": "Course Chapter",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "lesson.course",
|
||||
"fieldname": "course",
|
||||
"fieldtype": "Link",
|
||||
"label": "Course",
|
||||
"options": "LMS Course",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_tmwj",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "member",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Member",
|
||||
"options": "User",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "member.full_name",
|
||||
"fieldname": "member_name",
|
||||
"fieldtype": "Data",
|
||||
"label": "Member Name"
|
||||
},
|
||||
{
|
||||
"fetch_from": "member.user_image",
|
||||
"fieldname": "member_image",
|
||||
"fieldtype": "Attach Image",
|
||||
"label": "Member Image"
|
||||
},
|
||||
{
|
||||
"fetch_from": "member.username",
|
||||
"fieldname": "member_username",
|
||||
"fieldtype": "Data",
|
||||
"label": "Member Username"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_fywc",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "source",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Source",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_uuyv",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "watch_time",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Watch Time",
|
||||
"reqd": 1
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-06-30 16:57:10.561660",
|
||||
"modified_by": "sayali@frappe.io",
|
||||
"module": "LMS",
|
||||
"name": "LMS Video Watch Duration",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "LMS Student",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Moderator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Course Creator",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2025, Frappe and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class LMSVideoWatchDuration(Document):
|
||||
pass
|
||||
@@ -0,0 +1,21 @@
|
||||
# Copyright (c) 2025, Frappe and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
# On IntegrationTestCase, the doctype test records and all
|
||||
# link-field test record dependencies are recursively loaded
|
||||
# Use these module variables to add/remove to/from that list
|
||||
EXTRA_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
IGNORE_TEST_RECORD_DEPENDENCIES = [] # eg. ["User"]
|
||||
|
||||
|
||||
class IntegrationTestLMSVideoWatchDuration(IntegrationTestCase):
|
||||
"""
|
||||
Integration tests for LMSVideoWatchDuration.
|
||||
Use this class for testing interactions between multiple components.
|
||||
"""
|
||||
|
||||
pass
|
||||
@@ -1324,9 +1324,18 @@ def get_lesson(course, chapter, lesson):
|
||||
lesson_details.course_title = course_info.title
|
||||
lesson_details.paid_certificate = course_info.paid_certificate
|
||||
lesson_details.disable_self_learning = course_info.disable_self_learning
|
||||
lesson_details.videos = get_video_details(lesson_name)
|
||||
return lesson_details
|
||||
|
||||
|
||||
def get_video_details(lesson_name):
|
||||
return frappe.get_all(
|
||||
"LMS Video Watch Duration",
|
||||
{"lesson": lesson_name, "member": frappe.session.user},
|
||||
["source", "watch_time"],
|
||||
)
|
||||
|
||||
|
||||
def get_neighbour_lesson(course, chapter, lesson):
|
||||
numbers = []
|
||||
current = f"{chapter}.{lesson}"
|
||||
|
||||
Reference in New Issue
Block a user