Skip to content

Commit

Permalink
Merge pull request #49 from ts-tech-repo/course_logs
Browse files Browse the repository at this point in the history
Added course logs
  • Loading branch information
ts-tech-repo authored Jul 17, 2024
2 parents d84b17a + 5819d5b commit cfa8c9d
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 1 deletion.
153 changes: 153 additions & 0 deletions lms/djangoapps/instructor/views/course_log.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import logging
import json
import pytz

from bson.json_util import dumps
from django.contrib.auth.models import User
from django.views.decorators.csrf import csrf_exempt
from common.djangoapps.util.json_request import JsonResponse
from opaque_keys.edx.keys import CourseKey
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from django.views.decorators.clickjacking import xframe_options_exempt
from itertools import chain


log = logging.getLogger(__name__)



def get_course_unit_log(course_id):
course_key = CourseKey.from_string(course_id)
split_modulestore = modulestore()._get_modulestore_by_type(ModuleStoreEnum.Type.split)
active_version_collection = split_modulestore.db_connection.course_index
structure_collection = split_modulestore.db_connection.structures
course_definition = active_version_collection.find({"org" : course_key.org, "course" : course_key.course, "run" : course_key.run})

published_version = structure_collection.find_one({"_id" : course_definition[0]["versions"]["published-branch"]})

#fetch all previous versions
all_previous_versions = []
document = published_version

while True:
previous_version_id = document["previous_version"]
document = structure_collection.find_one({"_id": previous_version_id})

if not document:
break

all_previous_versions.append(document)

all_previous_versions = sorted(all_previous_versions, key=lambda x: x.get("edited_on", 0))

data = get_course_history(course_definition, published_version, all_previous_versions)
return data


def get_course_history(course_definition, published_version, all_previous_versions):
course_logs = {"last_updated_on" : conver_utc_ist(published_version["edited_on"]),
"components" : {}, "section_details" : {}, "subsection_details" : {}, "unit_details" : {}}

course_logs["latest_published_componenets"] = [block["block_id"] for block in published_version["blocks"] if block["block_type"] not in ("course", "course_info", "about", "chapter", "vertical", "sequential")]

for version in all_previous_versions:
course_logs = process_course_logs(version, course_logs)

course_logs = process_course_logs(published_version, course_logs)

return course_logs

def process_course_logs(version, course_logs):

components_list = []

for block in version["blocks"]:

if block["block_type"] not in ("course", "course_info", "about", "chapter", "vertical", "sequential", "static_tab"):
block_id = block["block_id"]

components_list.append(block_id)

parents_list, parents_names = find_block_parents(version, block_id)

edited_on = conver_utc_ist(block['edit_info']['edited_on'])
user_obj = User.objects.get(id = block["edit_info"]["edited_by"])

if block_id not in course_logs["components"]:
status = "Created"
course_logs["components"][block_id] = {"block_type" : block["block_type"].replace("_"," ").title(), "edited_info" : []}

else:

previous_block_info = course_logs["components"][block_id]["edited_info"][-1]

if block["definition"] != previous_block_info["definition"]:
status = "Edited"

elif block["definition"] == previous_block_info["definition"] and previous_block_info["fields"] != block["fields"]:
status = "Edited"

elif parents_list != previous_block_info["parents_list"]:
#add another obj as Moved Out with reference to -1 obj
course_logs["components"][block_id]["edited_info"].append({ "definition" : block["definition"], "fields" : block["fields"],
"parents_list" : previous_block_info["parents_list"],
"parents_names" : previous_block_info["parents_names"],
"edited_on" : edited_on, "edited_by" : user_obj.first_name,
"status" : "Moved Out"})
status = "Moved In"
else:
continue

course_logs["components"][block_id]["edited_info"].append({ "definition" : block["definition"], "fields" : block["fields"],
"parents_list" : parents_list, "parents_names" : parents_names,
"edited_on" : edited_on, "edited_by" : user_obj.first_name,
"status" : status})

#Check for Deleted Blocks
if components_list:
deleted_blocks = list(set(course_logs["components"].keys()) - set(components_list))

for d_block_id in deleted_blocks:
previous_block_info = course_logs["components"][d_block_id]["edited_info"][-1]

if previous_block_info["status"] != "Deleted" and d_block_id not in course_logs["latest_published_componenets"]:
user_obj = User.objects.get(id = version["edited_by"])
course_logs["components"][d_block_id]["edited_info"].append({ "definition" : previous_block_info["definition"],
"fields" : previous_block_info["fields"],
"parents_list" : previous_block_info["parents_list"],
"parents_names" : previous_block_info["parents_names"],
"edited_on" : conver_utc_ist(version["edited_on"]),
"edited_by" : user_obj.first_name, "status" : "Deleted"})
return course_logs

def find_block_parents(version, block_id):

for block in version["blocks"]:
if block["block_type"] == "vertical" and block_id in [i[-1] for i in block["fields"]["children"]]:
unit_name = block["fields"]["display_name"]
unit_id = block["block_id"]
break

for block in version["blocks"]:
if block["block_type"] == "sequential" and unit_id in [i[-1] for i in block["fields"]["children"]]:
subsection_name = block["fields"]["display_name"]
subsection_id = block["block_id"]
break

for block in version["blocks"]:
if block["block_type"] == "chapter" and subsection_id in [i[-1] for i in block["fields"]["children"]]:
section_name = block["fields"]["display_name"]
section_id = block["block_id"]
break

return [section_id, subsection_id, unit_id], [section_name, subsection_name, unit_name]


def conver_utc_ist(utc_datetime):
utc_timezone = pytz.timezone('UTC')
ist_timezone = pytz.timezone('Asia/Kolkata')
edited_info_ist = utc_datetime.astimezone(ist_timezone)
formated_ist = edited_info_ist.strftime("%b %d,%Y %H:%M:%S")
return formated_ist

13 changes: 12 additions & 1 deletion lms/djangoapps/instructor/views/instructor_dashboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
import requests
from lms.djangoapps.instructor_analytics.basic import enrolled_students_features

from lms.djangoapps.instructor.views.course_log import get_course_unit_log

log = logging.getLogger(__name__)


Expand Down Expand Up @@ -246,7 +248,7 @@ def instructor_dashboard_2(request, course_id): # lint-amnesty, pylint: disable
)

certificate_invalidations = CertificateInvalidation.get_certificate_invalidations(course_key)

sections.append(_section_course_log(course, access))
context = {
'course': course,
'studio_url': get_studio_url(course, 'course'),
Expand Down Expand Up @@ -806,6 +808,15 @@ def is_ecommerce_course(course_key):
sku_count = len([mode.sku for mode in CourseMode.modes_for_course(course_key) if mode.sku])
return sku_count > 0

def _section_course_log(course, access):
section_data = {
'section_key': 'course_log',
'section_display_name': _('Course Log'),
'access': access,
'course_id': str(course.id),
'course_logs' : get_course_unit_log(str(course.id))
}
return section_data

# For enrolled students
def _section_enrolled_students(course, access):
Expand Down
89 changes: 89 additions & 0 deletions lms/templates/instructor/instructor_dashboard_2/course_log.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<%page args="section_data" expression_filter="h"/>
<%!
from pytz import timezone
%>

<script>
var current_course_id = "${section_data['course_id']}"
</script>

%if section_data["course_logs"]["components"]:

<div>

<table class = "course_log" id='course_log'>
<thead>
<th>Unit</th>
<th>Type</th>
<th>Edited On</th>
<th>Edited By</th>
<th>Status</th>
</thead>
<tbody>
% for block_id, block_data in section_data["course_logs"]["components"].items():
% for edited_info in block_data["edited_info"]:
<tr>
<td>${edited_info["parents_names"][0]} &#x2192; ${edited_info["parents_names"][1]} &#x2192; ${edited_info["parents_names"][2]}</td>
<td>${block_data["block_type"]}</td>
<td>${edited_info["edited_on"]}</td>
<td>${edited_info["edited_by"]}</td>
<td>${edited_info["status"]}</td>
</tr>
% endfor


% endfor
</tbody>
</table>
%else:
<p> Content is not added.. </p>
%endif:
</div>

<script>
$(document).ready(function() {
var course_id_name = current_course_id.replaceAll("+", "-")
course_id_name = course_id_name.replaceAll(":", "-")
var course_log_file_name = "course_log_" + Math.floor(new Date() / 1000)

$('table#course_log').DataTable({
'columnDefs' : [{ 'targets':2, 'type':'date'},
{'targets':1, "render": function ( data, type, row ) {
switch (data) {
case 'Lti Consumer':
return 'Moodle Features';
break;
case 'Pdf':
return 'PDF';
break;
case 'Discussion':
return 'Discussion Forum'
break;
case 'Encryptplayer':
return 'Encrypt Video Player'
break;
default:
return data;
}}}],
'dom': 'Bfrtip',
'buttons': [{'extend': 'excel', 'text': 'Download', 'title': course_log_file_name}],
'search' : false,
'pageLength' : 50,
'lengthChange': false,
'info': false,
'paging': true,
'iDisplaylength': 25,
'aLengthMenu': [[100, 10, 25, 50, -1], [5, 10, 25, 50, "All"]],
"order": [[ 2, 'desc' ]]
//'scrollX': true
});
$('label:contains("Search:")').contents().filter(function() {
return this.nodeType === 3; // Text node
}).remove();
var intervalId_table = setInterval(function () {
$('#course_log.dataTable').wrap('<div class="wrapper_table"></div>');
clearInterval(intervalId_table);
}, 100);
});

</script>

0 comments on commit cfa8c9d

Please sign in to comment.