Added results CRUD and result detailed view
This commit is contained in:
		@@ -64,6 +64,10 @@ class Test:
 | 
				
			|||||||
        return test_code.replace('—', '')
 | 
					        return test_code.replace('—', '')
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    def delete(self):
 | 
					    def delete(self):
 | 
				
			||||||
 | 
					        test = db.tests.find_one({'_id': self._id})
 | 
				
			||||||
 | 
					        if 'entries' in test:
 | 
				
			||||||
 | 
					            if test['entries']:
 | 
				
			||||||
 | 
					                return jsonify({'error': 'Cannot delete an exam that has entries submitted to it.'}), 400
 | 
				
			||||||
        if self.dataset is None:
 | 
					        if self.dataset is None:
 | 
				
			||||||
            self.dataset = db.tests.find_one({'_id': self._id})['dataset']
 | 
					            self.dataset = db.tests.find_one({'_id': self._id})['dataset']
 | 
				
			||||||
        if db.tests.delete_one({'_id': self._id}):
 | 
					        if db.tests.delete_one({'_id': self._id}):
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -214,6 +214,11 @@ table.dataTable {
 | 
				
			|||||||
    font-size: 14pt;
 | 
					    font-size: 14pt;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.result-action-buttons {
 | 
				
			||||||
 | 
					    margin: 5px auto;
 | 
				
			||||||
 | 
					    width: fit-content;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Fallback for Edge
 | 
					/* Fallback for Edge
 | 
				
			||||||
-------------------------------------------------- */
 | 
					-------------------------------------------------- */
 | 
				
			||||||
@supports (-ms-ime-align: auto) {
 | 
					@supports (-ms-ime-align: auto) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -153,4 +153,43 @@ $('#dismiss-cookie-alert').click(function(event){
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    event.preventDefault();
 | 
					    event.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Script for Resutlt Actions
 | 
				
			||||||
 | 
					$('.result-action-buttons').click(function(event){
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var _id = $(this).data('_id')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if ($(this).data('result-action') == 'generate') {
 | 
				
			||||||
 | 
					        $.ajax({
 | 
				
			||||||
 | 
					            url: '/admin/certificate/',
 | 
				
			||||||
 | 
					            type: 'POST',
 | 
				
			||||||
 | 
					            data: JSON.stringify({'_id': _id}),
 | 
				
			||||||
 | 
					            contentType: 'application/json',
 | 
				
			||||||
 | 
					            dataType: 'html',
 | 
				
			||||||
 | 
					            success: function(response) {
 | 
				
			||||||
 | 
					                var display_window = window.open();
 | 
				
			||||||
 | 
					                display_window.document.write(response);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            error: function(response){
 | 
				
			||||||
 | 
					                error_response(response);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        var action = $(this).data('result-action')
 | 
				
			||||||
 | 
					        $.ajax({
 | 
				
			||||||
 | 
					            url: window.location.href,
 | 
				
			||||||
 | 
					            type: 'POST',
 | 
				
			||||||
 | 
					            data: JSON.stringify({'_id': _id, 'action': action}),
 | 
				
			||||||
 | 
					            contentType: 'application/json',
 | 
				
			||||||
 | 
					            success: function(response) {
 | 
				
			||||||
 | 
					                if (action == 'delete') {
 | 
				
			||||||
 | 
					                    window.location.href = '/admin/results/';
 | 
				
			||||||
 | 
					                } else window.location.reload();
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            error: function(response){
 | 
				
			||||||
 | 
					                error_response(response);
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
@@ -32,8 +32,10 @@
 | 
				
			|||||||
            {% block content %}{% endblock %}
 | 
					            {% block content %}{% endblock %}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <footer class="container site-footer">
 | 
					        <footer class="container site-footer mt-5">
 | 
				
			||||||
            {% include "admin/components/footer.html" %}
 | 
					            {% block footer %}
 | 
				
			||||||
 | 
					                {% include "admin/components/footer.html" %}
 | 
				
			||||||
 | 
					            {% endblock %}
 | 
				
			||||||
        </footer>
 | 
					        </footer>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- JQuery, Popper, and Bootstrap js dependencies -->
 | 
					        <!-- JQuery, Popper, and Bootstrap js dependencies -->
 | 
				
			||||||
@@ -70,5 +72,7 @@
 | 
				
			|||||||
        ></script>
 | 
					        ></script>
 | 
				
			||||||
        {% block datatable_scripts %}
 | 
					        {% block datatable_scripts %}
 | 
				
			||||||
        {% endblock %}
 | 
					        {% endblock %}
 | 
				
			||||||
 | 
					        {% block custom_data_script %}
 | 
				
			||||||
 | 
					        {% endblock %}
 | 
				
			||||||
    </body>
 | 
					    </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										84
									
								
								ref-test/admin/templates/admin/components/certificate.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								ref-test/admin/templates/admin/components/certificate.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					{% extends "admin/components/base.html" %}
 | 
				
			||||||
 | 
					{% block title %} SKA Referee Test | Detailed Results {% endblock %}
 | 
				
			||||||
 | 
					{% block navbar %}{% endblock %}
 | 
				
			||||||
 | 
					{% block top_alerts %}{% endblock %}
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					    <div class="d-flex justify-content-center">
 | 
				
			||||||
 | 
					        <h1 class="center">SKA Referee Theory Exam Results</h1>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div class="container mt-5">
 | 
				
			||||||
 | 
					        <div class="row justify-content-center">
 | 
				
			||||||
 | 
					            <div class="col-lg-6">
 | 
				
			||||||
 | 
					                <ul class="list-group">
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Candidate</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <h2>
 | 
				
			||||||
 | 
					                            {{ entry.name.surname}}, {{ entry.name.first_name }}
 | 
				
			||||||
 | 
					                        </h2>
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Email Address</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.email }}
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    {% if entry['club'] %}
 | 
				
			||||||
 | 
					                        <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                            <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                                <h5 class="mb-1">Club</h5>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            {{ entry.club }}
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Exam Code</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ '—'.join([entry.test_code[:4], entry.test_code[4:8], entry.test_code[8:]]) }}
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    {% if entry['user_code'] %}
 | 
				
			||||||
 | 
					                        <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                            <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                                <h5 class="mb-1">User Code</h5>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            {{ entry.user_code }}
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Start Time</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.start_time.strftime('%d %b %Y %H:%M:%S') }}
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Submission Time</h5>
 | 
				
			||||||
 | 
					                            {% if entry.status == 'late' %}
 | 
				
			||||||
 | 
					                                <span class="badge bg-danger">Late</span>
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.submission_time.strftime('%d %b %Y %H:%M:%S') }}
 | 
				
			||||||
 | 
					                    </li>       
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Score</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.results.score }}%
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action {% if entry.results.grade == 'fail' %}list-group-item-danger {% elif entry.results.grade == 'merit' %} list-group-item-success {% endif %}">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Grade</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.results.grade[0]|upper }}{{ entry.results.grade[1:]}}
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
 | 
					                <div class="site-footer mt-5">
 | 
				
			||||||
 | 
					                    These results were generated using the SKA RefTest web app on {{ now.strftime('%d %b %Y at %H:%M:%S') }}.
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                {% block footer %}{% endblock %}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/fixedheader/3.2.0/css/fixedHeader.bootstrap5.min.css"/>
 | 
					    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/fixedheader/3.2.0/css/fixedHeader.bootstrap5.min.css"/>
 | 
				
			||||||
    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/keytable/2.6.4/css/keyTable.bootstrap5.min.css"/>
 | 
					    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/keytable/2.6.4/css/keyTable.bootstrap5.min.css"/>
 | 
				
			||||||
    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/responsive/2.2.9/css/responsive.bootstrap5.min.css"/>
 | 
					    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/responsive/2.2.9/css/responsive.bootstrap5.min.css"/>
 | 
				
			||||||
 | 
					    <link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/searchbuilder/1.3.0/css/searchBuilder.dataTables.min.css"/>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
{% block datatable_scripts %}
 | 
					{% block datatable_scripts %}
 | 
				
			||||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js"></script>
 | 
					<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jszip/2.5.0/jszip.min.js"></script>
 | 
				
			||||||
@@ -23,5 +24,5 @@
 | 
				
			|||||||
<script type="text/javascript" src="https://cdn.datatables.net/keytable/2.6.4/js/dataTables.keyTable.min.js"></script>
 | 
					<script type="text/javascript" src="https://cdn.datatables.net/keytable/2.6.4/js/dataTables.keyTable.min.js"></script>
 | 
				
			||||||
<script type="text/javascript" src="https://cdn.datatables.net/responsive/2.2.9/js/dataTables.responsive.min.js"></script>
 | 
					<script type="text/javascript" src="https://cdn.datatables.net/responsive/2.2.9/js/dataTables.responsive.min.js"></script>
 | 
				
			||||||
<script type="text/javascript" src="https://cdn.datatables.net/responsive/2.2.9/js/responsive.bootstrap5.js"></script>
 | 
					<script type="text/javascript" src="https://cdn.datatables.net/responsive/2.2.9/js/responsive.bootstrap5.js"></script>
 | 
				
			||||||
    {% block custom_data_script %}{% endblock %}
 | 
					<script type="text/javascript" src="https://cdn.datatables.net/searchbuilder/1.3.0/js/dataTables.searchBuilder.min.js"></script>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
							
								
								
									
										175
									
								
								ref-test/admin/templates/admin/result-detail.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								ref-test/admin/templates/admin/result-detail.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,175 @@
 | 
				
			|||||||
 | 
					{% extends "admin/components/base.html" %}
 | 
				
			||||||
 | 
					{% block title %} SKA Referee Test | Detailed Results {% endblock %}
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					    {% include "admin/components/client-alerts.html" %}
 | 
				
			||||||
 | 
					    <h1>Exam Results</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div class="container">
 | 
				
			||||||
 | 
					        <div class="row justify-content-center">
 | 
				
			||||||
 | 
					            <div class="col-lg-6">          
 | 
				
			||||||
 | 
					                <ul class="list-group">
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Candidate</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <h2>
 | 
				
			||||||
 | 
					                            {{ entry.name.surname}}, {{ entry.name.first_name }}
 | 
				
			||||||
 | 
					                        </h2>
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Email Address</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.email }}
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    {% if entry['club'] %}
 | 
				
			||||||
 | 
					                        <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                            <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                                <h5 class="mb-1">Club</h5>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            {{ entry.club }}
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Exam Code</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ '—'.join([entry.test_code[:4], entry.test_code[4:8], entry.test_code[8:]]) }}
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    {% if entry['user_code'] %}
 | 
				
			||||||
 | 
					                        <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                            <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                                <h5 class="mb-1">User Code</h5>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            {{ entry.user_code }}
 | 
				
			||||||
 | 
					                        </li>
 | 
				
			||||||
 | 
					                    {% endif %}
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Start Time</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.start_time.strftime('%d %b %Y %H:%M:%S') }}
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Submission Time</h5>
 | 
				
			||||||
 | 
					                            {% if entry.status == 'late' %}
 | 
				
			||||||
 | 
					                                <span class="badge bg-danger">Late</span>
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.submission_time.strftime('%d %b %Y %H:%M:%S') }}
 | 
				
			||||||
 | 
					                    </li>       
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Score</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.results.score }}%
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                    <li class="list-group-item list-group-item-action {% if entry.results.grade == 'fail' %}list-group-item-danger {% elif entry.results.grade == 'merit' %} list-group-item-success {% endif %}">
 | 
				
			||||||
 | 
					                        <div class="d-flex w-100 justify-content-between">
 | 
				
			||||||
 | 
					                            <h5 class="mb-1">Grade</h5>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {{ entry.results.grade[0]|upper }}{{ entry.results.grade[1:]}}
 | 
				
			||||||
 | 
					                    </li>
 | 
				
			||||||
 | 
					                </ul>
 | 
				
			||||||
 | 
					                <div class="accordion" id="results-breakdown">
 | 
				
			||||||
 | 
					                    <div class="accordion-item">
 | 
				
			||||||
 | 
					                        <h2 class="accordion-header" id="by-category">
 | 
				
			||||||
 | 
					                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#by-category-breakdown" aria-expanded="false" aria-controls="by-category-breakdown">
 | 
				
			||||||
 | 
					                                Score By Categories
 | 
				
			||||||
 | 
					                            </button>
 | 
				
			||||||
 | 
					                        </h2>
 | 
				
			||||||
 | 
					                        <div id="by-category-breakdown" class="accordion-collapse collapse" aria-labelledby="by-category" data-bs-parent="#results-breakdown">
 | 
				
			||||||
 | 
					                            <div class="accordion-body">
 | 
				
			||||||
 | 
					                                <table class="table table-striped">
 | 
				
			||||||
 | 
					                                    <thead>
 | 
				
			||||||
 | 
					                                        <tr>
 | 
				
			||||||
 | 
					                                            <th>
 | 
				
			||||||
 | 
					                                                Category
 | 
				
			||||||
 | 
					                                            </th>
 | 
				
			||||||
 | 
					                                            <th>
 | 
				
			||||||
 | 
					                                                Score
 | 
				
			||||||
 | 
					                                            </th>
 | 
				
			||||||
 | 
					                                            <th>
 | 
				
			||||||
 | 
					                                                Max
 | 
				
			||||||
 | 
					                                            </th>
 | 
				
			||||||
 | 
					                                        </tr>
 | 
				
			||||||
 | 
					                                    </thead>
 | 
				
			||||||
 | 
					                                    <tbody>
 | 
				
			||||||
 | 
					                                        {% for tag, scores in entry.results.tags.items() %}
 | 
				
			||||||
 | 
					                                            <tr>
 | 
				
			||||||
 | 
					                                                <td>
 | 
				
			||||||
 | 
					                                                    {{ tag }}
 | 
				
			||||||
 | 
					                                                </td>
 | 
				
			||||||
 | 
					                                                <td>
 | 
				
			||||||
 | 
					                                                    {{ scores.scored }}
 | 
				
			||||||
 | 
					                                                </td>
 | 
				
			||||||
 | 
					                                                <td>
 | 
				
			||||||
 | 
					                                                    {{scores.max}}
 | 
				
			||||||
 | 
					                                                </td>
 | 
				
			||||||
 | 
					                                            </tr>
 | 
				
			||||||
 | 
					                                        {% endfor %}
 | 
				
			||||||
 | 
					                                    </tbody>
 | 
				
			||||||
 | 
					                                </table>
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="accordion-item">
 | 
				
			||||||
 | 
					                        <h2 class="accordion-header" id="by-question">
 | 
				
			||||||
 | 
					                            <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#by-question-breakdown" aria-expanded="false" aria-controls="by-question-breakdown">
 | 
				
			||||||
 | 
					                                View All Answers
 | 
				
			||||||
 | 
					                            </button>
 | 
				
			||||||
 | 
					                        </h2>
 | 
				
			||||||
 | 
					                    <div id="by-question-breakdown" class="accordion-collapse collapse" aria-labelledby="by-question" data-bs-parent="#results-breakdown">
 | 
				
			||||||
 | 
					                        <div class="accordion-body">
 | 
				
			||||||
 | 
					                            <table class="table table-striped">
 | 
				
			||||||
 | 
					                                    <thead>
 | 
				
			||||||
 | 
					                                        <tr>
 | 
				
			||||||
 | 
					                                            <th>
 | 
				
			||||||
 | 
					                                                Question
 | 
				
			||||||
 | 
					                                            </th>
 | 
				
			||||||
 | 
					                                            <th>
 | 
				
			||||||
 | 
					                                                Answer
 | 
				
			||||||
 | 
					                                            </th>
 | 
				
			||||||
 | 
					                                        </tr>
 | 
				
			||||||
 | 
					                                    </thead>
 | 
				
			||||||
 | 
					                                    <tbody>
 | 
				
			||||||
 | 
					                                        {% for question, answer in entry.answers.items() %}
 | 
				
			||||||
 | 
					                                            <tr>
 | 
				
			||||||
 | 
					                                                <td>
 | 
				
			||||||
 | 
					                                                    {{ question }}
 | 
				
			||||||
 | 
					                                                </td>
 | 
				
			||||||
 | 
					                                                <td>
 | 
				
			||||||
 | 
					                                                    {{ answer }}
 | 
				
			||||||
 | 
					                                                </td>
 | 
				
			||||||
 | 
					                                            </tr>
 | 
				
			||||||
 | 
					                                        {% endfor %}
 | 
				
			||||||
 | 
					                                    </tbody>
 | 
				
			||||||
 | 
					                            </table>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="container justify-content-center">
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                        <a href="#" class="btn btn-primary result-action-buttons" data-result-action="generate" data-_id="{{ entry._id }}">
 | 
				
			||||||
 | 
					                            <i class="bi bi-printer-fill button-icon"></i>
 | 
				
			||||||
 | 
					                            Printable Version
 | 
				
			||||||
 | 
					                        </a>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <div class="row">
 | 
				
			||||||
 | 
					                            {% if entry.status == 'late' %}
 | 
				
			||||||
 | 
					                                <a href="#" class="btn btn-warning result-action-buttons" data-result-action="override" data-_id="{{ entry._id }}">
 | 
				
			||||||
 | 
					                                    <i class="bi bi-clock-history button-icon"></i>
 | 
				
			||||||
 | 
					                                    Allow Late Entry
 | 
				
			||||||
 | 
					                                </a>
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        <a href="#" class="btn btn-danger result-action-buttons" data-result-action="delete" data-_id="{{ entry._id }}">
 | 
				
			||||||
 | 
					                            <i class="bi bi-trash-fill button-icon"></i>
 | 
				
			||||||
 | 
					                            Delete Result
 | 
				
			||||||
 | 
					                        </a>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
@@ -1 +1,138 @@
 | 
				
			|||||||
{% extends "admin/components/base.html" %}
 | 
					{% extends "admin/components/datatable.html" %}
 | 
				
			||||||
 | 
					{% block title %} SKA Referee Test | View Results {% endblock %}
 | 
				
			||||||
 | 
					{% block content %}
 | 
				
			||||||
 | 
					    {% include "admin/components/client-alerts.html" %}
 | 
				
			||||||
 | 
					    <h1>View Results</h1>
 | 
				
			||||||
 | 
					    {% if entries %}
 | 
				
			||||||
 | 
					        <table id="results-table" class="table table-striped" style="width:100%">
 | 
				
			||||||
 | 
					            <thead>
 | 
				
			||||||
 | 
					                <tr>
 | 
				
			||||||
 | 
					                    <th data-priority="1">
 | 
				
			||||||
 | 
					                        Name
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                    <th data-priority="4">
 | 
				
			||||||
 | 
					                        Club
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                    <th data-priority="5">
 | 
				
			||||||
 | 
					                        Exam Code
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                    <th data-priority="3">
 | 
				
			||||||
 | 
					                        Status
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                    <th data-priority="4">
 | 
				
			||||||
 | 
					                        Submitted
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                    <th data-priority="2">
 | 
				
			||||||
 | 
					                        Result
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                    <th data-priority="3">
 | 
				
			||||||
 | 
					                        Grade
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                    <th data-priority="1">
 | 
				
			||||||
 | 
					                        Details
 | 
				
			||||||
 | 
					                    </th>
 | 
				
			||||||
 | 
					                </tr>
 | 
				
			||||||
 | 
					            </thead>
 | 
				
			||||||
 | 
					            <tbody>
 | 
				
			||||||
 | 
					                {% for entry in entries %}
 | 
				
			||||||
 | 
					                    <tr class="table-row">
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
 | 
					                            {{ entry.name.surname }}, {{ entry.name.first_name }}
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
 | 
					                            {% if 'club' in entry %}
 | 
				
			||||||
 | 
					                                {{ entry.club }}
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
 | 
					                            {{ '—'.join([entry.test_code[:4], entry.test_code[4:8], entry.test_code[8:]]) }}
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
 | 
					                            {% if 'status' in entry %}
 | 
				
			||||||
 | 
					                                {{ entry.status }}
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
 | 
					                            {% if 'submission_time' in entry %}
 | 
				
			||||||
 | 
					                                {{ entry.submission_time.strftime('%d %b %Y') }}
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
 | 
					                            {% if 'results' in entry %}
 | 
				
			||||||
 | 
					                                {{ entry.results.score }}%
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                        <td>
 | 
				
			||||||
 | 
					                            {% if 'results' in entry %}
 | 
				
			||||||
 | 
					                                {{ entry.results.grade }}
 | 
				
			||||||
 | 
					                            {% endif %}
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                        <td class="row-actions">
 | 
				
			||||||
 | 
					                            <a
 | 
				
			||||||
 | 
					                                href="{{ url_for('admin_views.view_entry', _id = entry._id ) }}"
 | 
				
			||||||
 | 
					                                class="btn btn-primary entry-details"
 | 
				
			||||||
 | 
					                                data-_id="{{entry._id}}"
 | 
				
			||||||
 | 
					                                title="View Details"
 | 
				
			||||||
 | 
					                            >
 | 
				
			||||||
 | 
					                                <i class="bi bi-file-medical-fill button-icon"></i>
 | 
				
			||||||
 | 
					                            </button>
 | 
				
			||||||
 | 
					                        </td>
 | 
				
			||||||
 | 
					                    </tr>
 | 
				
			||||||
 | 
					                {% endfor %}
 | 
				
			||||||
 | 
					            </tbody>
 | 
				
			||||||
 | 
					        </table>
 | 
				
			||||||
 | 
					    {% else %}
 | 
				
			||||||
 | 
					            <div class="alert alert-primary alert-db-empty">
 | 
				
			||||||
 | 
					                <i class="bi bi-info-circle-fill" aria-title="Alert" title="Alert"></i>
 | 
				
			||||||
 | 
					                There are no exam attempts to view.
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					    {% endif %}
 | 
				
			||||||
 | 
					{% endblock %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% if entries %}
 | 
				
			||||||
 | 
					    {% block custom_data_script %}
 | 
				
			||||||
 | 
					        <script>
 | 
				
			||||||
 | 
					            $(document).ready(function() {
 | 
				
			||||||
 | 
					                $('#results-table').DataTable({
 | 
				
			||||||
 | 
					                    'searching': false,
 | 
				
			||||||
 | 
					                    'columnDefs': [
 | 
				
			||||||
 | 
					                        {'sortable': false, 'targets': [7]},
 | 
				
			||||||
 | 
					                        {'searchable': false, 'targets': [7]}
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'order': [[4, 'desc'], [0, 'asc']],
 | 
				
			||||||
 | 
					                    'buttons': [
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            extend: 'print',
 | 
				
			||||||
 | 
					                            exportOptions: {
 | 
				
			||||||
 | 
					                                columns: [0, 1, 3, 4, 5, 6]
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            extend: 'excel',
 | 
				
			||||||
 | 
					                            exportOptions: {
 | 
				
			||||||
 | 
					                                columns: [0, 1, 3, 4, 5, 6]
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        },
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            extend: 'pdf',
 | 
				
			||||||
 | 
					                            exportOptions: {
 | 
				
			||||||
 | 
					                                columns: [0, 1, 3, 4, 5, 6]
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    ],
 | 
				
			||||||
 | 
					                    'responsive': 'true',
 | 
				
			||||||
 | 
					                    'colReorder': 'true',
 | 
				
			||||||
 | 
					                    'fixedHeader': 'true',
 | 
				
			||||||
 | 
					                    'searchBuilder': {
 | 
				
			||||||
 | 
					                        depthLimit: 2,
 | 
				
			||||||
 | 
					                        columns: [1, 5, 6],
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    dom: 'BQlfrtip'
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					                // $('.buttons-pdf').html('<span class="glyphicon glyphicon-file" data-toggle="tooltip" title="Export To Excel"/>') -->
 | 
				
			||||||
 | 
					            } );
 | 
				
			||||||
 | 
					            $('#results-table').show();
 | 
				
			||||||
 | 
					            $(window).trigger('resize');
 | 
				
			||||||
 | 
					        </script>
 | 
				
			||||||
 | 
					    {% endblock %}
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
@@ -20,7 +20,7 @@
 | 
				
			|||||||
                        Author
 | 
					                        Author
 | 
				
			||||||
                    </th>
 | 
					                    </th>
 | 
				
			||||||
                    <th data-priority="3">
 | 
					                    <th data-priority="3">
 | 
				
			||||||
                        Use
 | 
					                        Exams
 | 
				
			||||||
                    </th>
 | 
					                    </th>
 | 
				
			||||||
                    <th data-priority="1">
 | 
					                    <th data-priority="1">
 | 
				
			||||||
                        Actions
 | 
					                        Actions
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
{% extends "admin/components/datatable.html" %}
 | 
					{% extends "admin/components/datatable.html" %}
 | 
				
			||||||
{% block title %} SKA Referee Test | Manage Exams {% endblock %}
 | 
					{% block title %} SKA Referee Test | Manage Exams {% endblock %}
 | 
				
			||||||
{% block content %}
 | 
					{% block content %}
 | 
				
			||||||
 | 
					    {% include "admin/components/client-alerts.html" %}
 | 
				
			||||||
    <h1>Manage Exams</h1>
 | 
					    <h1>Manage Exams</h1>
 | 
				
			||||||
    {% include "admin/components/secondary-navs/tests.html" %}
 | 
					    {% include "admin/components/secondary-navs/tests.html" %}
 | 
				
			||||||
    <h2>{{ display_title }}</h2>
 | 
					    <h2>{{ display_title }}</h2>
 | 
				
			||||||
@@ -157,7 +158,7 @@
 | 
				
			|||||||
            });
 | 
					            });
 | 
				
			||||||
            // $('.buttons-pdf').html('<span class="glyphicon glyphicon-file" data-toggle="tooltip" title="Export To Excel"/>') -->
 | 
					            // $('.buttons-pdf').html('<span class="glyphicon glyphicon-file" data-toggle="tooltip" title="Export To Excel"/>') -->
 | 
				
			||||||
        } );
 | 
					        } );
 | 
				
			||||||
        $('#test-table').show();
 | 
					        $('#active-test-table').show();
 | 
				
			||||||
        $(window).trigger('resize');
 | 
					        $(window).trigger('resize');
 | 
				
			||||||
    </script>
 | 
					    </script>
 | 
				
			||||||
    {% endblock %}
 | 
					    {% endblock %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
from flask import Blueprint, render_template, flash, redirect, request, jsonify, abort
 | 
					from flask import Blueprint, render_template, flash, redirect, request, jsonify, abort, make_response
 | 
				
			||||||
from flask.helpers import url_for
 | 
					from flask.helpers import url_for
 | 
				
			||||||
from functools import wraps
 | 
					from functools import wraps
 | 
				
			||||||
from datetime import datetime
 | 
					from datetime import datetime
 | 
				
			||||||
@@ -14,7 +14,7 @@ from main import app, db
 | 
				
			|||||||
from uuid import uuid4
 | 
					from uuid import uuid4
 | 
				
			||||||
import secrets
 | 
					import secrets
 | 
				
			||||||
from main import mail
 | 
					from main import mail
 | 
				
			||||||
from datetime import datetime, date, timedelta
 | 
					from datetime import datetime, date
 | 
				
			||||||
from .models.tests import Test
 | 
					from .models.tests import Test
 | 
				
			||||||
from common.data_tools import get_default_dataset, get_time_options, available_datasets
 | 
					from common.data_tools import get_default_dataset, get_time_options, available_datasets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -396,14 +396,65 @@ def create_test():
 | 
				
			|||||||
        return jsonify({ 'error': errors}), 400
 | 
					        return jsonify({ 'error': errors}), 400
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@views.route('/tests/delete/', methods=['POST'])
 | 
					@views.route('/tests/delete/', methods=['POST'])
 | 
				
			||||||
 | 
					@admin_account_required
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
def delete_test():
 | 
					def delete_test():
 | 
				
			||||||
    _id = request.get_json()['_id']
 | 
					    _id = request.get_json()['_id']
 | 
				
			||||||
    if db.tests.find_one({'_id': _id}):
 | 
					    if db.tests.find_one({'_id': _id}):
 | 
				
			||||||
        return Test(_id = _id).delete()
 | 
					        return Test(_id = _id).delete()
 | 
				
			||||||
    return jsonify({'error': 'Could not find the corresponding test to delete.'}), 404
 | 
					    return jsonify({'error': 'Could not find the corresponding test to delete.'}), 404
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@views.route('/test/<id>/', methods=['GET','POST'])
 | 
					@views.route('/test/<_id>/', methods=['GET','POST'])
 | 
				
			||||||
 | 
					@admin_account_required
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
def view_test(_id, filter=''):
 | 
					def view_test(_id, filter=''):
 | 
				
			||||||
    test = db.tests.find_one({'_id':_id})
 | 
					    test = db.tests.find_one({'_id':_id})
 | 
				
			||||||
    if not test:
 | 
					    if not test:
 | 
				
			||||||
        return abort(404)
 | 
					        return abort(404)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@views.route('/results/')
 | 
				
			||||||
 | 
					@admin_account_required
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def view_entries():
 | 
				
			||||||
 | 
					    entries = decrypt_find(db.entries, {})
 | 
				
			||||||
 | 
					    return render_template('/admin/results.html', entries = entries)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@views.route('/results/<_id>/', methods = ['GET', 'POST'])
 | 
				
			||||||
 | 
					@admin_account_required
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def view_entry(_id=''):
 | 
				
			||||||
 | 
					    entry = decrypt_find_one(db.entries, {'_id': _id})
 | 
				
			||||||
 | 
					    if request.method == 'GET':
 | 
				
			||||||
 | 
					        if not entry:
 | 
				
			||||||
 | 
					            return abort(404)
 | 
				
			||||||
 | 
					        return render_template('/admin/result-detail.html', entry = entry)
 | 
				
			||||||
 | 
					    if request.method == 'POST':
 | 
				
			||||||
 | 
					        if not entry:
 | 
				
			||||||
 | 
					            return jsonify({'error': 'A valid entry could no be found.'}), 404
 | 
				
			||||||
 | 
					        action = request.get_json()['action']
 | 
				
			||||||
 | 
					        if action == 'override':
 | 
				
			||||||
 | 
					            late_ignore = db.entries.find_one_and_update({'_id': _id}, {'$set': {'status': 'late (allowed)'}})
 | 
				
			||||||
 | 
					            if late_ignore:
 | 
				
			||||||
 | 
					                flash('Late status for the entry has been allowed.', 'success')
 | 
				
			||||||
 | 
					                return jsonify({'success': 'Late status allowed.'}), 200
 | 
				
			||||||
 | 
					            return jsonify({'error': 'An error occurred.'}), 400
 | 
				
			||||||
 | 
					        if action == 'delete':
 | 
				
			||||||
 | 
					            test_code = entry['test_code']
 | 
				
			||||||
 | 
					            test = db.tests.find_one_and_update({'test_code': test_code}, {'$pull': {'entries': _id}})
 | 
				
			||||||
 | 
					            if not test:
 | 
				
			||||||
 | 
					                return jsonify({'error': 'A valid exam could not be found.'}), 404
 | 
				
			||||||
 | 
					            delete = db.entries.delete_one({'_id': _id})
 | 
				
			||||||
 | 
					            if delete:
 | 
				
			||||||
 | 
					                flash('Entry has been deleted.', 'success')
 | 
				
			||||||
 | 
					                return jsonify({'success': 'Entry has been deleted.'}), 200
 | 
				
			||||||
 | 
					            return jsonify({'error': 'An error occurred.'}), 400
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@views.route('/certificate/', methods=['POST'])
 | 
				
			||||||
 | 
					@admin_account_required
 | 
				
			||||||
 | 
					@login_required
 | 
				
			||||||
 | 
					def generate_certificate():
 | 
				
			||||||
 | 
					    _id = request.get_json()['_id']
 | 
				
			||||||
 | 
					    entry = decrypt_find_one(db.entries, {'_id': _id})
 | 
				
			||||||
 | 
					    if not entry:
 | 
				
			||||||
 | 
					        return abort(404)
 | 
				
			||||||
 | 
					    return render_template('/admin/components/certificate.html', entry = entry)
 | 
				
			||||||
@@ -172,6 +172,11 @@ body {
 | 
				
			|||||||
    font-size: 26pt;
 | 
					    font-size: 26pt;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.button-icon {
 | 
				
			||||||
 | 
					    font-size: 20px;
 | 
				
			||||||
 | 
					    margin-right: 2px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Change Autocomplete styles in Chrome*/
 | 
					/* Change Autocomplete styles in Chrome*/
 | 
				
			||||||
input:-webkit-autofill,
 | 
					input:-webkit-autofill,
 | 
				
			||||||
input:-webkit-autofill:hover, 
 | 
					input:-webkit-autofill:hover, 
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -363,7 +363,7 @@ function render_question() {
 | 
				
			|||||||
function check_answered() {
 | 
					function check_answered() {
 | 
				
			||||||
    var question = questions[current_question];
 | 
					    var question = questions[current_question];
 | 
				
			||||||
    var name = question.q_no;
 | 
					    var name = question.q_no;
 | 
				
			||||||
    if (question_status[current_question] == 0) {
 | 
					    if (question_status[current_question] == 0 || question_status[current_question] == -1) {
 | 
				
			||||||
        if (!$(`input[name='${name}']:checked`).val()) {
 | 
					        if (!$(`input[name='${name}']:checked`).val()) {
 | 
				
			||||||
            question_status[current_question] = -1;
 | 
					            question_status[current_question] = -1;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,9 @@
 | 
				
			|||||||
        The presentation of the questions is just a start, and we acknowledge we still have a long way to go. We welcome any feedback on how we can further improve this test.
 | 
					        The presentation of the questions is just a start, and we acknowledge we still have a long way to go. We welcome any feedback on how we can further improve this test.
 | 
				
			||||||
    </p>
 | 
					    </p>
 | 
				
			||||||
    <div class="button-container">
 | 
					    <div class="button-container">
 | 
				
			||||||
        <a href="{{ url_for('quiz_views.instructions') }}" class="btn btn-success">Read the Instructions</a>
 | 
					        <a href="{{ url_for('quiz_views.instructions') }}" class="btn btn-success">
 | 
				
			||||||
 | 
					            <i class="bi bi-book-fill button-icon"></i>
 | 
				
			||||||
 | 
					            Read the Instructions
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
@@ -50,6 +50,9 @@
 | 
				
			|||||||
        </p>
 | 
					        </p>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <div class="button-container">
 | 
					    <div class="button-container">
 | 
				
			||||||
        <a href="{{ url_for('quiz_views.start') }}" class="btn btn-success">Take the Quiz</a>
 | 
					        <a href="{{ url_for('quiz_views.start') }}" class="btn btn-success">
 | 
				
			||||||
 | 
					            <i class="bi bi-pencil-fill button-icon"></i>
 | 
				
			||||||
 | 
					            Take the Exam
 | 
				
			||||||
 | 
					        </a>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
{% endblock %}
 | 
					{% endblock %}
 | 
				
			||||||
@@ -9,10 +9,10 @@
 | 
				
			|||||||
        <span class="surname">{{ entry.name.surname }}</span>, {{ entry.name.first_name }}
 | 
					        <span class="surname">{{ entry.name.surname }}</span>, {{ entry.name.first_name }}
 | 
				
			||||||
    </h3>
 | 
					    </h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <strong class="results-details">Email Address</strong>: {{ entry.email }}
 | 
					    <strong class="results-details">Email Address</strong>: {{ entry.email }} <br />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% if entry.club %}
 | 
					    {% if entry.club %}
 | 
				
			||||||
        <strong class="results-details">Club</strong>: {{ entry.club }}
 | 
					        <strong class="results-details">Club</strong>: {{ entry.club }} <br />
 | 
				
			||||||
    {% endif%}
 | 
					    {% endif%}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    {% if entry.status == 'late' %}
 | 
					    {% if entry.status == 'late' %}
 | 
				
			||||||
@@ -34,7 +34,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            <ul>
 | 
					            <ul>
 | 
				
			||||||
                {% for tag in tag_output %}
 | 
					                {% for tag in tag_output %}
 | 
				
			||||||
                    <li>{{ tag[0] }}</li>
 | 
					                    <li>{{ tag }}</li>
 | 
				
			||||||
                {% endfor %}
 | 
					                {% endfor %}
 | 
				
			||||||
            </ul>
 | 
					            </ul>
 | 
				
			||||||
        {% endif %}
 | 
					        {% endif %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,7 +34,10 @@
 | 
				
			|||||||
            <div class="container form-submission-button">
 | 
					            <div class="container form-submission-button">
 | 
				
			||||||
                <div class="row">
 | 
					                <div class="row">
 | 
				
			||||||
                    <div class="col text-center">
 | 
					                    <div class="col text-center">
 | 
				
			||||||
                        <button class="btn btn-md btn-success btn-block" type="submit">Start Quiz</button>
 | 
					                        <button class="btn btn-md btn-success btn-block" type="submit">
 | 
				
			||||||
 | 
					                            <i class="bi bi-pencil-fill button-icon"></i>
 | 
				
			||||||
 | 
					                            Start Exam
 | 
				
			||||||
 | 
					                        </button>
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -104,6 +104,7 @@ def fetch_questions():
 | 
				
			|||||||
        'end_time': end_time
 | 
					        'end_time': end_time
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    entry = db.entries.find_one_and_update({'_id': _id}, {'$set': update}, upsert=False, return_document=ReturnDocument.AFTER)
 | 
					    entry = db.entries.find_one_and_update({'_id': _id}, {'$set': update}, upsert=False, return_document=ReturnDocument.AFTER)
 | 
				
			||||||
 | 
					    db.tests.find_one_and_update({'_id': test['_id']}, {'$push': {'entries': _id}})
 | 
				
			||||||
    dataset = test['dataset']
 | 
					    dataset = test['dataset']
 | 
				
			||||||
    dataset_path = os.path.join(app.config['DATA_FILE_DIRECTORY'], dataset)
 | 
					    dataset_path = os.path.join(app.config['DATA_FILE_DIRECTORY'], dataset)
 | 
				
			||||||
    with open(dataset_path, 'r') as data_file:
 | 
					    with open(dataset_path, 'r') as data_file:
 | 
				
			||||||
@@ -172,47 +173,48 @@ def result():
 | 
				
			|||||||
    elif entry['results']['grade'] == 'fail':
 | 
					    elif entry['results']['grade'] == 'fail':
 | 
				
			||||||
        flavour_text_plain = """Unfortunately, you were not successful in passing the theory exam in this attempt. We hope that this does not dissuade you, and that you try again in the future.
 | 
					        flavour_text_plain = """Unfortunately, you were not successful in passing the theory exam in this attempt. We hope that this does not dissuade you, and that you try again in the future.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
    email = Message(
 | 
					    if not entry['status'] == 'late':
 | 
				
			||||||
        subject="SKA Refereeing Theory Exam Results",
 | 
					        email = Message(
 | 
				
			||||||
        recipients=[entry['email']],
 | 
					            subject="SKA Refereeing Theory Exam Results",
 | 
				
			||||||
        body=f"""SKA Refereeing Theory Exam\n\n
 | 
					            recipients=[entry['email']],
 | 
				
			||||||
        Candidate Results\n\n
 | 
					            body=f"""SKA Refereeing Theory Exam\n\n
 | 
				
			||||||
        Dear {entry['name']['first_name']},\n\n
 | 
					            Candidate Results\n\n
 | 
				
			||||||
        This email is to confirm that you have took the SKA Refereeing Theory Exam. Your test has been evaluated and your results have been generated.\n\n
 | 
					            Dear {entry['name']['first_name']},\n\n
 | 
				
			||||||
        {entry['name']['surname']}, {entry['name']['first_name']}\n\n
 | 
					            This email is to confirm that you have took the SKA Refereeing Theory Exam. Your test has been evaluated and your results have been generated.\n\n
 | 
				
			||||||
        Email Address: {entry['email']}\n
 | 
					            {entry['name']['surname']}, {entry['name']['first_name']}\n\n
 | 
				
			||||||
        {f"Club: {entry['club']}" if entry['club'] else ''}\n
 | 
					            Email Address: {entry['email']}\n
 | 
				
			||||||
        Date of Test: {entry['submission_time'].strftime('%d %b %Y')}\n
 | 
					            {f"Club: {entry['club']}" if entry['club'] else ''}\n
 | 
				
			||||||
        Score: {score}%\n
 | 
					            Date of Test: {entry['submission_time'].strftime('%d %b %Y')}\n
 | 
				
			||||||
        Grade: {entry['results']['grade']}\n\n
 | 
					            Score: {score}%\n
 | 
				
			||||||
        {flavour_text_plain}\n\n
 | 
					            Grade: {entry['results']['grade']}\n\n
 | 
				
			||||||
        Based on your answers, we would also suggest you brush up on the following topics as you continue refereeing:\n\n
 | 
					            {flavour_text_plain}\n\n
 | 
				
			||||||
        {','.join(tag_output)}\n\n
 | 
					            Based on your answers, we would also suggest you brush up on the following topics as you continue refereeing:\n\n
 | 
				
			||||||
        Thank you for taking the time to get qualified as a referee.\n\n
 | 
					            {','.join(tag_output)}\n\n
 | 
				
			||||||
        Best wishes,\n
 | 
					            Thank you for taking the time to get qualified as a referee.\n\n
 | 
				
			||||||
        SKA Refereeing
 | 
					            Best wishes,\n
 | 
				
			||||||
        """,
 | 
					            SKA Refereeing
 | 
				
			||||||
        html=f"""<h1>SKA Refereeing Theory Exam</h1>
 | 
					            """,
 | 
				
			||||||
        <h2>Candidate Results</h2>
 | 
					            html=f"""<h1>SKA Refereeing Theory Exam</h1>
 | 
				
			||||||
        <p>Dear {entry['name']['first_name']},</p>
 | 
					            <h2>Candidate Results</h2>
 | 
				
			||||||
        <p>This email is to confirm that you have took the SKA Refereeing Theory Exam. Your test has been evaluated and your results have been generated.</p>
 | 
					            <p>Dear {entry['name']['first_name']},</p>
 | 
				
			||||||
        <h3>{entry['name']['surname']}, {entry['name']['first_name']}</h3>
 | 
					            <p>This email is to confirm that you have took the SKA Refereeing Theory Exam. Your test has been evaluated and your results have been generated.</p>
 | 
				
			||||||
        <p><strong>Email Address</strong>: {entry['email']}</p>
 | 
					            <h3>{entry['name']['surname']}, {entry['name']['first_name']}</h3>
 | 
				
			||||||
        {f"<p><strong>Club</strong>: {entry['club']}</p>" if entry['club'] else ''}
 | 
					            <p><strong>Email Address</strong>: {entry['email']}</p>
 | 
				
			||||||
        <p><strong>Date of Test</strong>: {entry['submission_time'].strftime('%d %b %Y')}</p>
 | 
					            {f"<p><strong>Club</strong>: {entry['club']}</p>" if entry['club'] else ''}
 | 
				
			||||||
        <h1>{score}%</h1>
 | 
					            <p><strong>Date of Test</strong>: {entry['submission_time'].strftime('%d %b %Y')}</p>
 | 
				
			||||||
        <h2>{entry['results']['grade']}</h2>
 | 
					            <h1>{score}%</h1>
 | 
				
			||||||
        <p>{flavour_text_plain}</p>
 | 
					            <h2>{entry['results']['grade']}</h2>
 | 
				
			||||||
        <p>Based on your answers, we would also suggest you revise the following topics as you continue refereeing:</p>
 | 
					            <p>{flavour_text_plain}</p>
 | 
				
			||||||
        <ul>
 | 
					            <p>Based on your answers, we would also suggest you revise the following topics as you continue refereeing:</p>
 | 
				
			||||||
        <li>{'</li><li>'.join(tag_output)}</li>
 | 
					            <ul>
 | 
				
			||||||
        </ul>
 | 
					            <li>{'</li><li>'.join(tag_output)}</li>
 | 
				
			||||||
        <p>Thank you for taking the time to get qualified as a referee.</p>
 | 
					            </ul>
 | 
				
			||||||
        <p>Best wishes,<br />
 | 
					            <p>Thank you for taking the time to get qualified as a referee.</p>
 | 
				
			||||||
        SKA Refereeing</p>
 | 
					            <p>Best wishes,<br />
 | 
				
			||||||
        """,
 | 
					            SKA Refereeing</p>
 | 
				
			||||||
    )
 | 
					            """,
 | 
				
			||||||
    mail.send(email)
 | 
					        )
 | 
				
			||||||
 | 
					        mail.send(email)
 | 
				
			||||||
    return render_template('/quiz/result.html', entry=entry, score=score, tag_output=tag_output)
 | 
					    return render_template('/quiz/result.html', entry=entry, score=score, tag_output=tag_output)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@views.route('/privacy/')
 | 
					@views.route('/privacy/')
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user