Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create training basics --> Add create training from admin console by uploading json #1950

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2b39e31
Create Template for Training APP
superryeti Mar 28, 2023
743a13e
Create Model for Course,tracking user progress
superryeti Mar 28, 2023
99bfa07
Create Courses with json
superryeti Mar 28, 2023
674278b
Course training with json examples and fixtures update
superryeti Mar 28, 2023
647a1e9
Manage courses from admin console
superryeti Mar 28, 2023
e3c382b
add migration file
superryeti Apr 10, 2023
04bbc38
Resolving the comments : Adding a few checks & refactoring code in tr…
Rutvikrj26 Jun 10, 2023
4394837
Fixing the unused variable assignment.
Rutvikrj26 Jun 14, 2023
346f067
Refactoring Serializers Complete
Rutvikrj26 Jun 15, 2023
f37c602
Apply suggestions from code review in guidelines_course template
Rutvikrj26 Jul 19, 2023
87733e8
Apply suggestions from code review in training models
Rutvikrj26 Jul 19, 2023
5aad9e0
updating version type
Rutvikrj26 Jul 19, 2023
7b07b0b
Fixed the permissions and contraints indent
Rutvikrj26 Jul 31, 2023
30fac53
Updated Migration
Rutvikrj26 Aug 1, 2023
3201e69
Styling Changes
Rutvikrj26 Aug 2, 2023
215adea
Added warning message on training page if the user_training is about …
Rutvikrj26 Aug 4, 2023
ad11e1a
removing accidental changes
Rutvikrj26 Aug 4, 2023
777d97f
Fixing styling changes
Rutvikrj26 Aug 4, 2023
90b7fd4
Update the training download view and basic UI changes in Console's t…
Rutvikrj26 Aug 28, 2023
eaf2d59
Merged Migrations due to multiple migrations error
Rutvikrj26 Aug 28, 2023
25f6f63
Rebasing onto Physionet dev
Rutvikrj26 Sep 13, 2023
0faeb90
Fixed Version Comparision
Rutvikrj26 Sep 13, 2023
fbd14c4
addressed docstring comment
Rutvikrj26 Nov 6, 2023
b332cad
implemented validate_version and simplified code for major version ch…
Rutvikrj26 Nov 8, 2023
683564e
added the logic for version check in validators
Rutvikrj26 Nov 20, 2023
0b1df68
Fixed the course download functionality
Rutvikrj26 Nov 22, 2023
471bc0e
added the UI & views for ability to expire/download individual course…
Rutvikrj26 Nov 26, 2023
bcd997e
Added the form to collect the number of days left for expiry in the f…
Rutvikrj26 Nov 26, 2023
57bb692
Running the migrations merge
Rutvikrj26 Nov 27, 2023
b0db135
Updated the tests for latest paths
Rutvikrj26 Nov 27, 2023
644c99c
Fixing styling issues
Rutvikrj26 Nov 27, 2023
673fd2a
Fixing styling issues
Rutvikrj26 Jan 23, 2024
ba4df7f
seperated the create & update training into respective pages.
Rutvikrj26 Jan 24, 2024
30c5687
fixing styling changes
Rutvikrj26 Jan 24, 2024
0bc4fa0
Updated the sample json files to accurately reflect the current schema
Rutvikrj26 Feb 7, 2024
fa78607
Cleaned the files and added a brief explanation on the field
Rutvikrj26 Feb 8, 2024
4695be2
Updated Schema for Trainings and associated updates in views & models
Rutvikrj26 Feb 21, 2024
d584b29
Added docstrings for the training-type explaining the usage of the model
Rutvikrj26 Feb 26, 2024
ddb7375
Added the slug backfill and updated the routes to work with slug rath…
Rutvikrj26 Mar 4, 2024
83b4c91
Fixed issues for failing tests : Added a custom save function to gene…
Rutvikrj26 Mar 4, 2024
e3bc003
Fixing styling issues
Rutvikrj26 Mar 4, 2024
578f8a2
Added a relation to trainings in course and updated the course expir…
Rutvikrj26 Mar 7, 2024
eaa8e14
updated the migration to implement random number based slug backfilling
Rutvikrj26 Mar 7, 2024
de3e23c
fixed the slug field to fix the unique issues.
Rutvikrj26 Mar 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions physionet-django/console/navbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,8 @@ def get_menu_items(self, request):
NavLink(_('Training check'), 'training_list', 'school',
view_args=['review']),

NavLink(_('Courses'), 'courses', 'chalkboard-teacher'),

NavSubmenu(_('Events'), 'events', 'clipboard-list', [
NavLink(_('Active'), 'event_active'),
NavLink(_('Archived'), 'event_archive'),
Expand Down
72 changes: 34 additions & 38 deletions physionet-django/console/templates/console/console_navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,37 @@
<ul class="navbar-nav navbar-sidenav" id="sideAccordion">
{% console_nav_menu_items request as nav_menu_items %}
{% for item in nav_menu_items %}
{% if item.subitems %}
<li class="nav-item">
<a id="nav_{{ item.name }}_dropdown"
class="nav-link nav-link-collapse drop {{ item.active|yesno:',collapsed' }}"
data-toggle="collapse"
data-target="#nav_{{ item.name }}_components"
data-parent="#sideAccordion"
aria-expanded="{{ item.active|yesno:'true,false' }}"
href="#">
{% if item.icon %}
<span class="nav-link-icon fa fa-fw fa-{{ item.icon }}"></span>
{% endif %}
<span class="nav-link-text">{{ item.title }}</span>
</a>
<ul class="sidenav-second-level collapse {{ item.active|yesno:'show,' }}"
id="nav_{{ item.name }}_components">
{% for subitem in item.subitems %}
<li class="nav-item {{ subitem.active|yesno:'active,' }}">
<a id="nav_{{ subitem.name }}" class="nav-link"
href="{{ subitem.url }}">{{ subitem.title }}</a>
</li>
{% endfor %}
</ul>
</li>
{% else %}
<li class="nav-item {{ item.active|yesno:'active,' }}">
<a id="nav_{{ item.name }}" class="nav-link" href="{{ item.url }}">
{% if item.icon %}
<span class="nav-link-icon fa fa-fw fa-{{ item.icon }}"></span>
{% endif %}
<span class="nav-link-text">{{ item.title }}</span>
</a>
{% if item.subitems %}
<li class="nav-item">
<a id="nav_{{ item.name }}_dropdown"
class="nav-link nav-link-collapse drop {{ item.active|yesno:',collapsed' }}" data-toggle="collapse"
data-target="#nav_{{ item.name }}_components" data-parent="#sideAccordion"
aria-expanded="{{ item.active|yesno:'true,false' }}" href="#">
{% if item.icon %}
<span class="nav-link-icon fa fa-fw fa-{{ item.icon }}"></span>
{% endif %}
<span class="nav-link-text">{{ item.title }}</span>
</a>
<ul class="sidenav-second-level collapse {{ item.active|yesno:'show,' }}" id="nav_{{ item.name }}_components">
{% for subitem in item.subitems %}
<li class="nav-item {{ subitem.active|yesno:'active,' }}">
<a id="nav_{{ subitem.name }}" class="nav-link" href="{{ subitem.url }}">{{ subitem.title }}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</li>
{% else %}
<li class="nav-item {{ item.active|yesno:'active,' }}">
<a id="nav_{{ item.name }}" class="nav-link" href="{{ item.url }}">
{% if item.icon %}
<span class="nav-link-icon fa fa-fw fa-{{ item.icon }}"></span>
{% endif %}
<span class="nav-link-text">{{ item.title }}</span>
</a>
</li>
{% endif %}
{% endfor %}
<!-- end of menu items -->
<!-- end of menu items -->
</ul>

<ul class="navbar-nav sidenav-toggler">
Expand All @@ -64,10 +59,11 @@
</div>
<div class="navbar-search">
<form class="form-inline" action="{% url 'content_index' %}">
<input name="topic" class="search-input" type="text" placeholder="Search">
<span class="input-group-btn">
<button id="search-button" type="submit" class="btn-search my-2 my-sm-0" type="button"><i class="fa fa-search"></i></button>
</span>
<input name="topic" class="search-input" type="text" placeholder="Search">
<span class="input-group-btn">
<button id="search-button" type="submit" class="btn-search my-2 my-sm-0" type="button"><i
class="fa fa-search"></i></button>
</span>
</form>
</div>
</nav>
162 changes: 162 additions & 0 deletions physionet-django/console/templates/console/guidelines_course.html
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 %}
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>
Loading
Loading