forked from openedx/edx-platform
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #49 from ts-tech-repo/course_logs
Added course logs
- Loading branch information
Showing
3 changed files
with
254 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
89 changes: 89 additions & 0 deletions
89
lms/templates/instructor/instructor_dashboard_2/course_log.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]} → ${edited_info["parents_names"][1]} → ${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> |