-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
create training basics --> Add create training from admin console by …
…uploading json (#1950) ## What? Here i have added a feature that will allow admin to create training from admin console by uploading json file. This also lays foundation to implement take training feature and resume training feature for user later. ## Why? A training feature was desired by HDN so that we can build/customize a onplatform training for users which will ultimately be used to control access to projects. The new onplatform training will work just like the existing Trainings and work with projects without any additional changes, Currently users take training outside the platform and then upload the certificate to the platform. ## How? We created a separate app called Training. This app will be used to create/manage training courses, and allow users to take onplatform training. We created two major types of Models in training.models **1. Platform Training (`training.models.OnPlatformTraining`)** The idea here is that a new Training Course will be defined with `user.models.TrainingType`. This is the ultimate(top) model for a training course. The training content for `TrainingType` model will be implemented by the new training app. On Training app, `OnPlatformTraining` instance can be created for each TrainingType. For different versions of the same training course, we can create as many `OnPlatformTraining` models as we want as long as the version is different. A training is divided into modules. Each module has a description and a list of contents and quizzes. Modules are like chapters in a book. Each module has a list of contents and quizzes. Contents are like paragraphs in a chapter. Quizzes are like questions in a chapter. Contents and quizes can be organized in any order which is controlled by `order` field. For ordering the modules, contents and quizzes, we have used `order` field. The ordering is unique for each instance of the parent model. For example : Under Module 1, we have content 1(order=1), content 2(order=2), content 3(order=4) and quiz 1(order=3). Then the ordering of the contents and quizzes will be content 1, content 2, quiz 1, content 3. It is expected that the order will start from 1 and will be incremented by 1 with no gaps. **2. Tracking User Progress during training** (`training.models.OnPlatformTrainingProgress`) When a user starts a training, a `OnPlatformTrainingProgress` instance should be created for that user and the version of the training type that they are taking. This model tracks the progress of the user during the training. Similarly, when a user starts a module, a `ModuleProgress` model should be created for each module in the training. For quiz, content progress, we should create a instance of `CompletedContent` or `CompletedQuiz` model when the user completes a content or quiz. I don't think we need to track when someone started a content or quiz as these are expected to complete in few minutes. ## Testing? Next PR #1951 ## Screenshots (optional) ## Anything Else? ### How to create a training from admin console? 1. Login as admin 2. Navigate to `Training` tab in admin console 3. Click on `Create/Update OP Training` button 4. Select `Create New Training` from the dropdown 5. Upload the example json from `training/fixtures/example-training-create.json` ### How to edit a training from admin console? 1. Follow steps 1-3 from above 2. Select Existing Training from the dropdown 3. Upload the example json from `training/fixtures/example-training-update.json` Notes: This is the first part of Training PR, there is another PR #1951 which is rebased on this PR.
- Loading branch information
Showing
38 changed files
with
2,074 additions
and
47 deletions.
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
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
162 changes: 162 additions & 0 deletions
162
physionet-django/console/templates/console/guidelines_course.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,162 @@ | ||
{% extends "console/base_console.html" %} | ||
{% load static %} | ||
{% block title %}Guidelines for Courses{% endblock %} | ||
|
||
{% block content %} | ||
<div class="card mb-3"> | ||
<div class="card-header"> | ||
Guidelines for creating and updating courses | ||
</div> | ||
<div class="card-body"> | ||
<div class="table-responsive"> | ||
<p>To create a new course or update a course, you will need to organize all the course content in a json file | ||
and upload via the | ||
<a href="{% url 'courses' %}">Courses</a> page.</p> | ||
<p>Here is the <a href="{% static 'sample/create-course-schema.json' %}">course schema</a> with typing info that you can | ||
follow to create a new course or update a course. Similarly, here is an example json file to <a | ||
href="{% static 'sample/example-course-create.json' %}">create a new course</a> or to <a | ||
href="{% static 'sample/example-course-update.json' %}">update a course</a></p> | ||
|
||
<h4>General Schema Explanation</h4> | ||
<pre> | ||
{ | ||
"name": "string", | ||
"description": "string", | ||
"valid_duration": "string", | ||
"courses": [{ | ||
"version": "string", | ||
"modules": [ | ||
{ | ||
"contents": [ | ||
{ | ||
"body": "string", | ||
"order": "integer" | ||
} | ||
], | ||
"quizzes": [ | ||
{ | ||
"question": "string", | ||
"order": "integer", | ||
"choices": [ | ||
{ | ||
"body": "string", | ||
"is_correct": "boolean" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] | ||
}] | ||
} | ||
</pre> | ||
<ol start="1"> | ||
<li><strong>Course Information:</strong> Fill out all fields in the JSON file with the appropriate information | ||
about your course, including: | ||
</li> | ||
<ul> | ||
<li>The name of the course</li> | ||
<li>A description of the course in html</li> | ||
<li>The valid duration of the course</li> | ||
<li>The version of the course</li> | ||
</ul> | ||
<small><strong>Note the importance of versioning:</strong> It's essential to keep track of the version number | ||
of your course. | ||
Please make sure the version number is unique for each course update. Even minor changes, such as a spelling | ||
correction, | ||
require an update to the version number in the new JSON file (refer to the create and update JSON examples | ||
above). | ||
Please use the Semantic Versioning system (Major.Minor) for version numbers. | ||
If you update a course's major version (e.g., from 1.5 to 2.0), all existing training certificates provided | ||
to | ||
users will expire after a month. To regain access to resources, users must retake the new version of the | ||
training.</small> | ||
|
||
</ol> | ||
<ol start="2"> | ||
<li><strong>Modules:</strong> A course may have one or more modules. Each module contains the course content and quizzes. | ||
Modules must include: | ||
section of the course. Each module should include: | ||
</li> | ||
<ul> | ||
<li>A name for the module</li> | ||
<li>A description of the module in html</li> | ||
<li>An order for the module</li> | ||
<li>One or more content items in the module</li> | ||
<li>One or more quizzes in the module</li> | ||
</ul> | ||
</ol> | ||
<ol start="3"> | ||
<li><strong>Content:</strong> A module may have one or more content blocks. Each content block should include:</li> | ||
<ul> | ||
<li>A body for the content in html</li> | ||
<li>An order for the content</li> | ||
</ul> | ||
</ol> | ||
<ol start="4"> | ||
<li><strong>Quizzes:</strong> A module may have one or more quiz. Each Quiz should include:</li> | ||
<ul> | ||
<li>A question for the quiz</li> | ||
<li>An order for the quiz</li> | ||
<li>One or more choices for the quiz, including the correct answer</li> | ||
<small><strong>Order:</strong> Note that the order is used to determine which content or quiz comes first, | ||
so please | ||
make sure you order the content and quiz properly. <br> For example, here the user will see the content | ||
and quiz in the following order | ||
content1, quiz 1, content 2, quiz 2. | ||
<pre><code> | ||
"contents": [ | ||
{ | ||
"body": "This content will display first for the given module.", | ||
"order": 1 | ||
}, | ||
{ | ||
"body": "This content will appear third (order = 3), after the quiz below.", | ||
"order": 3 | ||
} | ||
], | ||
"quizzes": [ | ||
{ | ||
"question": "This quiz will appear after the first content block (order = 2).", | ||
"order": 2, | ||
"choices": [ | ||
{ | ||
"body": "This answer is correct. The user will proceed to the next block if they answer it.", | ||
"is_correct": true | ||
}, | ||
{ | ||
"body": "This choice is incorrect. The user will return to the beginning of the module if they answer it.", | ||
"is_correct": false | ||
} | ||
] | ||
}, | ||
{ | ||
"question": "This quiz will appear last, after the final content block (order = 4).", | ||
"order": 4, | ||
"choices": [ | ||
{ | ||
"body": "choice 1", | ||
"is_correct": true | ||
}, | ||
{ | ||
"body": "choice 2", | ||
"is_correct": false | ||
} | ||
] | ||
} | ||
] | ||
</code></pre> | ||
</small> | ||
</ul> | ||
</ol> | ||
<ol start="5"> | ||
<li><strong>Choices:</strong> A quiz may have one or more choices. Each choice should include:</li> | ||
<ul> | ||
<li>A body for the choice</li> | ||
<li>A boolean value of true for the correct answer</li> | ||
</ul> | ||
</ol> | ||
</div> | ||
</div> | ||
</div> | ||
{% endblock %} |
126 changes: 126 additions & 0 deletions
126
physionet-django/console/templates/console/training_type/course_details.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,126 @@ | ||
{% extends "console/base_console.html" %} | ||
|
||
{% load static %} | ||
|
||
{% block title %}{{ training_type }}{% endblock %} | ||
|
||
{% block content %} | ||
<script> | ||
console.log('update-course event listener triggered'); | ||
$('#update-course').on('show.bs.modal', function (event) { | ||
console.log("modal opened"); | ||
var button = $(event.relatedTarget); // Button that triggered the modal | ||
var trainingType = button.data('training-type'); // Extract info from data-* attributes | ||
var modal = $(this); | ||
modal.find('.modal-body input#training-type').val(trainingType); | ||
console.log(trainingType); | ||
}); | ||
</script> | ||
|
||
<div class="card mb-3"> | ||
<div class="card-header"> | ||
{{ training_type }} <span class="badge badge-pill badge-info"></span> | ||
</div> | ||
|
||
<div class="card mb-3"> | ||
<div class="card-body"> | ||
<div><button type="button" class="btn btn-sm btn-success" data-toggle="modal" | ||
data-target="#create-course">Update</button></div> | ||
<div class="modal fade" id="create-course" tabindex="-1"> | ||
<div class="modal-dialog"> | ||
<form action="{% url 'course_details' training_type.slug %}" method="POST" enctype="multipart/form-data" class=""> | ||
<div class="modal-content"> | ||
<div class="modal-body"> | ||
<p>Learn how to create a course <a href="{% url 'guidelines_course' %}">here</a> or see sample files to <a | ||
href="{% static 'sample/example-course-create.json' %}">create a new course</a></p> | ||
<div> | ||
{% csrf_token %} | ||
<div class="form-group"> | ||
<label>Training Type: </label> | ||
<option value="{{ training_type.slug }}">{{ training_type.name|title }}</option> | ||
</div> | ||
<div class="form-group"> | ||
<label>File: </label> | ||
<input type="file" name="json_file" id="json_file" required="True" class="form-control"> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="modal-footer"> | ||
<input type="submit" name="create" class="btn btn-primary"> | ||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button> | ||
</div> | ||
</div> | ||
</form> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<br> | ||
<h3 class="card-title text-center">Active Versions</h3> | ||
<div class="card-body"> | ||
<table class="table table-bordered"> | ||
<thead> | ||
<tr> | ||
<th>Name</th> | ||
<th>Version</th> | ||
<th>Download</th> | ||
<th>Expire</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for course in active_course_versions %} | ||
|
||
<tr> | ||
<td>{{ training_type.name|title }}</td> | ||
<td>{{ course.version }}</td> | ||
<td> | ||
<a href="{% url 'download_course_version' training_type.slug course.version %}"><i | ||
class="fa fa-download"></i> Download</a> | ||
</td> | ||
<td> | ||
<form action="{% url 'expire_course_version' training_type.slug course.version %}" method="POST"> | ||
{% csrf_token %} | ||
<input type="date" name="expiry_date"> | ||
<button class="btn btn-sm btn-danger" type="submit"> Expire</button> | ||
</form> | ||
</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
<p><b>Note:</b> Users that have taken the particular version of the course that is getting expired, | ||
will need to retake the course or else they will loose credentialing after the number of | ||
days specified above while expiring the course. </p> | ||
</div> | ||
<h3 class="card-title text-center">Archived Versions</h3> | ||
<div class="card-body"> | ||
<table class="table table-bordered"> | ||
<thead> | ||
<tr> | ||
<th>Name</th> | ||
<th>Version</th> | ||
<th>Action</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for course in inactive_course_versions %} | ||
|
||
<tr> | ||
<td>{{ training_type.name|title }}</td> | ||
<td>{{ course.version }}</td> | ||
<td> | ||
<a href="{% url 'download_course_version' training_type.slug course.version %}"><i | ||
class="fa fa-download"></i> Download</a> | ||
</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
{% endblock %} | ||
|
||
<script> | ||
document.getElementById('expiry_date').valueAsDate = new Date(); | ||
</script> |
Oops, something went wrong.