add simple survey support
This commit is contained in:
@@ -361,6 +361,8 @@ class CaseCollectionForm(ModelForm):
|
||||
"question_time_limit",
|
||||
"answer_entry_grace_period",
|
||||
"prerequisites",
|
||||
"pre_survey",
|
||||
"post_survey",
|
||||
Fieldset(
|
||||
"Custom Start Screen",
|
||||
"start_screen_content",
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Generated by Django 6.0.1 on 2026-06-28 20:59
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('atlas', '0108_multiusersession'),
|
||||
('survey', '__first__'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='casecollection',
|
||||
name='post_survey',
|
||||
field=models.ForeignKey(blank=True, help_text='Survey to be filled out AFTER completing this collection/exam.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_post_surveys', to='survey.survey'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='casecollection',
|
||||
name='pre_survey',
|
||||
field=models.ForeignKey(blank=True, help_text='Survey to be filled out BEFORE taking this collection/exam.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='%(app_label)s_%(class)s_pre_surveys', to='survey.survey'),
|
||||
),
|
||||
]
|
||||
@@ -51,6 +51,18 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if post_survey_url %}
|
||||
<div class="alert alert-info border-info bg-dark bg-opacity-25 my-3 d-flex align-items-center justify-content-between" role="alert">
|
||||
<div>
|
||||
<h5 class="alert-heading text-info mb-1"><i class="bi bi-patch-question me-2"></i>Post-Collection Feedback</h5>
|
||||
<p class="mb-0 small text-muted">Please take a moment to complete the feedback survey for this case collection.</p>
|
||||
</div>
|
||||
<a href="{{ post_survey_url }}" class="btn btn-info btn-sm text-dark fw-semibold px-4">
|
||||
Take Survey <i class="bi bi-arrow-right ms-1"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="list-group mb-3">
|
||||
{% for question, answer, self_review, review_stats, timing_state in question_answer_tuples %}
|
||||
<div class="list-group-item {% if review_stats.has_outstanding_feedback %}border-warning border-2{% elif not answer %}bg-danger-subtle border-danger-subtle{% endif %}">
|
||||
|
||||
@@ -75,5 +75,25 @@
|
||||
|
||||
|
||||
|
||||
<h3>Surveys & Feedback</h3>
|
||||
<p>The platform supports reusable and shareable feedback forms and surveys that can be configured by administrators and staff to assess educational improvement and collect feedback.</p>
|
||||
|
||||
<h5>Managing Surveys (Staff/Admins)</h5>
|
||||
<ul>
|
||||
<li><strong>Create Surveys:</strong> Navigate to the <em>Survey Dashboard</em> to create a survey, define its name, description, and decide if responses should be anonymous.</li>
|
||||
<li><strong>Question Types:</strong> You can add questions of three types:
|
||||
<ul>
|
||||
<li><em>Free Text:</em> For qualitative comments.</li>
|
||||
<li><em>Multiple Choice:</em> For single-select questions (entered one option per line).</li>
|
||||
<li><em>Rating Scale:</em> 1-to-5 Likert scale ratings.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>Question Reordering:</strong> Adjust question ordering using the numeric inputs and click <em>Update Order</em>.</li>
|
||||
<li><strong>Linking to Case Collections & Exams:</strong> Expose surveys by assigning them as <em>Pre-take</em> or <em>Post-take</em> surveys in the collection/exam edit screens. Candidates are automatically redirected to complete the Pre-take survey before they can begin, and are prompted to complete the Post-take survey once they finish.</li>
|
||||
<li><strong>Analytics & CSV Export:</strong> Click <em>Analytics</em> on the dashboard to view aggregated rating averages, response counts, graphical progress bar breakdowns, and free-text replies. You can export complete response details to CSV format by clicking <em>Export CSV</em>.</li>
|
||||
</ul>
|
||||
|
||||
<h5>Taking Standalone/General Surveys</h5>
|
||||
<p>Surveys can also be distributed as standalone links (e.g. <code>/survey/take/<id>/</code>) for general platform feedback or course evaluation not tied to a specific exam.</p>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
+39
-1
@@ -112,6 +112,7 @@ from .forms import (
|
||||
UserQuestionAnswerForm,
|
||||
UserReportAnswerForm,
|
||||
)
|
||||
from survey.views import check_survey_interception
|
||||
from generic.models import UserHiddenItem
|
||||
from .models import (
|
||||
Case,
|
||||
@@ -6930,7 +6931,6 @@ def collection_take_start(request, pk, cid=None, passcode=None):
|
||||
_type_: _description_
|
||||
"""
|
||||
collection = get_object_or_404(CaseCollection, pk=pk)
|
||||
|
||||
try:
|
||||
collection.check_user_can_take(cid, passcode, request.user)
|
||||
valid_user = True
|
||||
@@ -6947,6 +6947,11 @@ def collection_take_start(request, pk, cid=None, passcode=None):
|
||||
{'message': str(e), 'prereq': prereq, 'cid': cid, 'passcode': passcode},
|
||||
)
|
||||
|
||||
# Check if pre-survey is required and not yet taken
|
||||
response = check_survey_interception(request, collection, cid, passcode)
|
||||
if response:
|
||||
return response
|
||||
|
||||
|
||||
#valid_user = collection.check_logged_in_user(request)
|
||||
|
||||
@@ -7729,6 +7734,32 @@ def collection_take_overview(
|
||||
else:
|
||||
question_answer_tuples.append((cd, None, self_review, review_stats, timing_state))
|
||||
|
||||
post_survey_url = None
|
||||
if collection.post_survey:
|
||||
user_param = request.user if request.user.is_authenticated else None
|
||||
cid_param = int(cid) if cid and str(cid).isdigit() else None
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from survey.models import SurveyResponse
|
||||
ct = ContentType.objects.get_for_model(collection)
|
||||
|
||||
has_responded = SurveyResponse.objects.filter(
|
||||
survey=collection.post_survey,
|
||||
user=user_param,
|
||||
cid=cid_param,
|
||||
content_type=ct,
|
||||
object_id=collection.pk,
|
||||
pre_or_post=SurveyResponse.SurveyContext.POST
|
||||
).exists()
|
||||
|
||||
if not has_responded:
|
||||
from django.urls import reverse
|
||||
survey_url = reverse("survey:survey_take", kwargs={"pk": collection.post_survey.pk})
|
||||
params = f"?pre_or_post=POST&content_type_id={ct.id}&object_id={collection.pk}&next={request.get_full_path()}"
|
||||
if cid and passcode:
|
||||
params += f"&cid={cid}&passcode={passcode}"
|
||||
post_survey_url = f"{survey_url}{params}"
|
||||
|
||||
return render(
|
||||
request,
|
||||
"atlas/collection_take_overview.html",
|
||||
@@ -7743,6 +7774,7 @@ def collection_take_overview(
|
||||
"outstanding_case_count": outstanding_case_count,
|
||||
"total_outstanding_feedback": total_outstanding_feedback,
|
||||
"end_overview_resources": collection.end_overview_resources.all(),
|
||||
"post_survey_url": post_survey_url,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -8368,6 +8400,12 @@ def collection_case_view_take(
|
||||
"""
|
||||
collection = get_object_or_404(CaseCollection, pk=pk)
|
||||
|
||||
# Check if pre-survey is required and not yet taken
|
||||
from survey.views import check_survey_interception
|
||||
response = check_survey_interception(request, collection, cid, passcode)
|
||||
if response:
|
||||
return response
|
||||
|
||||
# If caller provided a case_id (case PK) instead of a case_number (index),
|
||||
# translate it into the index used by collection.get_case_by_index().
|
||||
if case_id is not None and case_number is None:
|
||||
|
||||
Reference in New Issue
Block a user