mirror of
https://cdm-project.com/Download-Tools/udemy-downloader.git
synced 2025-04-30 00:54:25 +02:00
Add functionality to parse coding assignments
This commit is contained in:
parent
875d888503
commit
35673f91e0
118
coding_assignment_template.html
Normal file
118
coding_assignment_template.html
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Coding Assignment</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: sf pro text, -apple-system, BlinkMacSystemFont, Roboto,
|
||||||
|
segoe ui, Helvetica, Arial, sans-serif, apple color emoji,
|
||||||
|
segoe ui emoji, segoe ui symbol;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 22.4px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
p, ul, ol {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
.code-snippet {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #d1d7dc;
|
||||||
|
color: #b4690e;
|
||||||
|
font-size: 90%;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
}
|
||||||
|
.code-block {
|
||||||
|
background-color: #fff;
|
||||||
|
color: #b4690e;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
.black-block {
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
.italic-text {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body onload="main()">
|
||||||
|
<h1 id="coding-title"></h1>
|
||||||
|
<div>
|
||||||
|
<h2>Instructions</h2>
|
||||||
|
<div id="coding-instructions"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Test(s)</h2>
|
||||||
|
<div id="coding-tests"></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Solution(s)</h2>
|
||||||
|
<div id="coding-solutions"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const quizData = __data_placeholder__;
|
||||||
|
|
||||||
|
function renderCodeList(rootElement, codeList, className, titlePrefix) {
|
||||||
|
for (var i = 0; i < codeList.length; i++) {
|
||||||
|
var elem = codeList[i];
|
||||||
|
var jsElem = document.createElement("div");
|
||||||
|
jsElem.className = className;
|
||||||
|
var jsElemTitle = document.createElement("h3");
|
||||||
|
jsElemTitle.innerHTML = titlePrefix + " " + (i + 1);
|
||||||
|
var jsElemBody = document.createElement("code");
|
||||||
|
jsElemBody.className = "code-block black-block";
|
||||||
|
jsElemBody.innerHTML = "<pre>" + elem.content + "</pre>";
|
||||||
|
jsElem.appendChild(jsElemTitle);
|
||||||
|
jsElem.appendChild(jsElemBody);
|
||||||
|
rootElement.appendChild(jsElem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
// display the assignment
|
||||||
|
var codingTitle = document.getElementById("coding-title");
|
||||||
|
codingTitle.innerHTML = quizData.title;
|
||||||
|
|
||||||
|
var codingInstructions = document.getElementById("coding-instructions");
|
||||||
|
if (quizData.hasInstructions) {
|
||||||
|
codingInstructions.innerHTML = quizData.instructions;
|
||||||
|
} else {
|
||||||
|
codingInstructions.innerHTML = "<span class=\"italic-text\">" + quizData.instructions
|
||||||
|
+ "</span>";
|
||||||
|
}
|
||||||
|
|
||||||
|
// display the test(s)
|
||||||
|
var codingTests = document.getElementById("coding-tests");
|
||||||
|
if (!quizData.hasTests) {
|
||||||
|
codingTests.innerHTML = "<span class=\"italic-text\">" + quizData.tests + "</span>";
|
||||||
|
} else {
|
||||||
|
renderCodeList(codingTests, quizData.tests, "coding-test", "Test");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// display the solution(s)
|
||||||
|
var codingSolutions = document.getElementById("coding-solutions");
|
||||||
|
if (!quizData.hasSolutions) {
|
||||||
|
codingSolutions.innerHTML = "<span class=\"italic-text\">" + quizData.solutions + "</span>";
|
||||||
|
} else {
|
||||||
|
renderCodeList(codingSolutions, quizData.solutions, "coding-solution", "Solution");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
68
main.py
68
main.py
@ -385,6 +385,38 @@ class Udemy:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
return resp.get("results")
|
return resp.get("results")
|
||||||
|
|
||||||
|
def _get_elem_value_or_none(self, elem, key):
|
||||||
|
return elem[key] if elem and key in elem else "(None)"
|
||||||
|
|
||||||
|
def _get_quiz_with_info(self, quiz_id):
|
||||||
|
resp = { "_class": None, "_type": None, "contents": None }
|
||||||
|
quiz_json = self._get_quiz(quiz_id)
|
||||||
|
is_only_one = len(quiz_json) == 1 and quiz_json[0]["_class"] == "assessment"
|
||||||
|
is_coding_assignment = quiz_json[0]["assessment_type"] == "coding-problem"
|
||||||
|
|
||||||
|
resp["_class"] = quiz_json[0]["_class"]
|
||||||
|
|
||||||
|
if is_only_one and is_coding_assignment:
|
||||||
|
assignment = quiz_json[0]
|
||||||
|
prompt = assignment["prompt"]
|
||||||
|
|
||||||
|
resp["_type"] = assignment["assessment_type"]
|
||||||
|
|
||||||
|
resp["contents"] = {
|
||||||
|
"instructions": self._get_elem_value_or_none(prompt, "instructions"),
|
||||||
|
"tests": self._get_elem_value_or_none(prompt, "test_files"),
|
||||||
|
"solutions": self._get_elem_value_or_none(prompt, "solution_files"),
|
||||||
|
}
|
||||||
|
|
||||||
|
resp["hasInstructions"] = False if resp["contents"]["instructions"] == "(None)" else True
|
||||||
|
resp["hasTests"] = False if isinstance(resp["contents"]["tests"], str) else True
|
||||||
|
resp["hasSolutions"] = False if isinstance(resp["contents"]["solutions"], str) else True
|
||||||
|
else: # Normal quiz
|
||||||
|
resp["_type"] = "normal-quiz"
|
||||||
|
resp["contents"] = quiz_json
|
||||||
|
|
||||||
|
return resp
|
||||||
|
|
||||||
def _extract_supplementary_assets(self, supp_assets, lecture_counter):
|
def _extract_supplementary_assets(self, supp_assets, lecture_counter):
|
||||||
_temp = []
|
_temp = []
|
||||||
@ -1606,18 +1638,50 @@ def process_lecture(lecture, lecture_path, lecture_file_name, chapter_dir):
|
|||||||
|
|
||||||
|
|
||||||
def process_quiz(udemy: Udemy, lecture, chapter_dir):
|
def process_quiz(udemy: Udemy, lecture, chapter_dir):
|
||||||
|
quiz = udemy._get_quiz_with_info(lecture.get("id"))
|
||||||
|
if quiz["_type"] == "coding-problem":
|
||||||
|
process_coding_assignment(quiz, lecture, chapter_dir)
|
||||||
|
else: # Normal quiz
|
||||||
|
process_normal_quiz(quiz, lecture, chapter_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def process_normal_quiz(quiz, lecture, chapter_dir):
|
||||||
lecture_title = lecture.get("lecture_title")
|
lecture_title = lecture.get("lecture_title")
|
||||||
lecture_index = lecture.get("lecture_index")
|
lecture_index = lecture.get("lecture_index")
|
||||||
lecture_file_name = sanitize_filename(lecture_title + ".html")
|
lecture_file_name = sanitize_filename(lecture_title + ".html")
|
||||||
lecture_path = os.path.join(chapter_dir, lecture_file_name)
|
lecture_path = os.path.join(chapter_dir, lecture_file_name)
|
||||||
|
|
||||||
logger.info(f" > Processing quiz {lecture_index}")
|
logger.info(f" > Processing quiz {lecture_index}")
|
||||||
questions = udemy._get_quiz(lecture.get("id"))
|
|
||||||
with open("quiz_template.html", "r") as f:
|
with open("quiz_template.html", "r") as f:
|
||||||
html = f.read()
|
html = f.read()
|
||||||
quiz_data = {
|
quiz_data = {
|
||||||
"pass_percent": lecture.get("data").get("pass_percent"),
|
"pass_percent": lecture.get("data").get("pass_percent"),
|
||||||
"questions": questions,
|
"questions": quiz["contents"],
|
||||||
|
}
|
||||||
|
html = html.replace("__data_placeholder__", json.dumps(quiz_data))
|
||||||
|
with open(lecture_path, "w") as f:
|
||||||
|
f.write(html)
|
||||||
|
|
||||||
|
|
||||||
|
def process_coding_assignment(quiz, lecture, chapter_dir):
|
||||||
|
lecture_title = lecture.get("lecture_title")
|
||||||
|
lecture_index = lecture.get("lecture_index")
|
||||||
|
lecture_file_name = sanitize_filename(lecture_title + ".html")
|
||||||
|
lecture_path = os.path.join(chapter_dir, lecture_file_name)
|
||||||
|
|
||||||
|
logger.info(f" > Processing quiz {lecture_index} (coding assignment)")
|
||||||
|
|
||||||
|
with open("coding_assignment_template.html", "r") as f:
|
||||||
|
html = f.read()
|
||||||
|
quiz_data = {
|
||||||
|
"title": lecture_title,
|
||||||
|
"hasInstructions": quiz["hasInstructions"],
|
||||||
|
"hasTests": quiz["hasTests"],
|
||||||
|
"hasSolutions": quiz["hasSolutions"],
|
||||||
|
"instructions": quiz["contents"]["instructions"],
|
||||||
|
"tests": quiz["contents"]["tests"],
|
||||||
|
"solutions": quiz["contents"]["solutions"],
|
||||||
}
|
}
|
||||||
html = html.replace("__data_placeholder__", json.dumps(quiz_data))
|
html = html.replace("__data_placeholder__", json.dumps(quiz_data))
|
||||||
with open(lecture_path, "w") as f:
|
with open(lecture_path, "w") as f:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user