Add exam collection link to the navigation menu and clean up dropdown toggle
This commit is contained in:
@@ -4709,6 +4709,67 @@ class ExamCollectionClone(CreateView, AuthorRequiredMixin):
|
|||||||
|
|
||||||
object.author.set(self.author)
|
object.author.set(self.author)
|
||||||
object.save()
|
object.save()
|
||||||
|
|
||||||
|
# Clone exams linked to the original collection into the new one.
|
||||||
|
# For each supported exam app, find exams related to the old collection
|
||||||
|
# and create shallow copies (questions/authors re-applied, but
|
||||||
|
# active/archive/publish flags cleared).
|
||||||
|
if hasattr(self, "initial_object") and self.initial_object:
|
||||||
|
for _label, rel_name, ExamModel in GROUP_TYPES:
|
||||||
|
try:
|
||||||
|
old_exams = list(getattr(self.initial_object, rel_name).all())
|
||||||
|
except Exception:
|
||||||
|
old_exams = []
|
||||||
|
|
||||||
|
for old_exam in old_exams:
|
||||||
|
# Build a dict of field values excluding the PK
|
||||||
|
data = model_to_dict(old_exam, exclude=["id"])
|
||||||
|
|
||||||
|
# Clear group membership and flags for cloned exams
|
||||||
|
data["valid_user_users"] = [] if "valid_user_users" in data else []
|
||||||
|
data["valid_cid_users"] = [] if "valid_cid_users" in data else []
|
||||||
|
data["cid_user_groups"] = [] if "cid_user_groups" in data else []
|
||||||
|
data["user_user_groups"] = [] if "user_user_groups" in data else []
|
||||||
|
|
||||||
|
data["active"] = False
|
||||||
|
# some models use `archive` or `archived` - try both safely
|
||||||
|
if "archive" in data:
|
||||||
|
data["archive"] = False
|
||||||
|
if "archived" in data:
|
||||||
|
data["archived"] = False
|
||||||
|
data["publish_results"] = False if "publish_results" in data else data.get("publish_results", False)
|
||||||
|
|
||||||
|
# Keep a copy of M2M fields to reapply after create
|
||||||
|
m2m_backup = {}
|
||||||
|
for m2m_field in ("exam_questions", "author", "markers", "valid_user_users", "valid_cid_users", "cid_user_groups", "user_user_groups"):
|
||||||
|
if m2m_field in data:
|
||||||
|
m2m_backup[m2m_field] = data.pop(m2m_field)
|
||||||
|
|
||||||
|
# Set the new examcollection foreign key to the newly created collection
|
||||||
|
# model_to_dict returns the FK id; replace with instance for create()
|
||||||
|
data["examcollection"] = object
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_exam = ExamModel.objects.create(**data)
|
||||||
|
except Exception:
|
||||||
|
# If creation failed for any model-specific field, skip cloning this exam
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Reapply M2M relationships where possible
|
||||||
|
for field_name, ids in m2m_backup.items():
|
||||||
|
try:
|
||||||
|
getattr(new_exam, field_name).set(ids)
|
||||||
|
except Exception:
|
||||||
|
# If the field doesn't exist on this model, ignore
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Some exam models provide an ordering helper
|
||||||
|
if hasattr(new_exam, "order_questions"):
|
||||||
|
new_exam.order_questions()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
return HttpResponseRedirect(object.get_absolute_url())
|
return HttpResponseRedirect(object.get_absolute_url())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -201,7 +201,7 @@
|
|||||||
<a class="nav-link active" href="{% url 'atlas:index' %}">Atlas</a>
|
<a class="nav-link active" href="{% url 'atlas:index' %}">Atlas</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item dropdown pt-0">
|
<li class="nav-item dropdown pt-0">
|
||||||
<a class="nav-link dropdown-toggle active" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
FRCR
|
FRCR
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
@@ -223,6 +223,9 @@
|
|||||||
<li>
|
<li>
|
||||||
<a class="dropdown-item" href="{% url 'sbas:index' %}">SBAs</a>
|
<a class="dropdown-item" href="{% url 'sbas:index' %}">SBAs</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="{% url 'generic:examcollection_list' %}">Exam Collection</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
{% if request.user|has_group:"cid_user_manager" %}
|
{% if request.user|has_group:"cid_user_manager" %}
|
||||||
@@ -334,7 +337,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
//}
|
//}
|
||||||
// fallback to any table on the page that looks like it has selection
|
// fallback to any table on the page that looks like it has selection
|
||||||
var any = document.querySelector('table.js-row-selectable') //|| Array.from(document.querySelectorAll('table')).find(function(t){ return t.querySelector && (t.querySelector('input[name="selection"]') || t.querySelector('input[type="checkbox"]')); });
|
var any = document.querySelector('table.js-row-selectable') //|| Array.from(document.querySelectorAll('table')).find(function(t){ return t.querySelector && (t.querySelector('input[name="selection"]') || t.querySelector('input[type="checkbox"]')); });
|
||||||
console.log('findTableForControl fallback', any);
|
|
||||||
return any || null;
|
return any || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +377,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
container.parentNode.insertBefore(wrapper, container);
|
container.parentNode.insertBefore(wrapper, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
//// Create controls for any table that looks selectable but lacks a control block
|
// Create controls for any table that looks selectable but lacks a control block
|
||||||
Array.from(document.querySelectorAll('table')).forEach(function(t){
|
Array.from(document.querySelectorAll('table')).forEach(function(t){
|
||||||
if (t.querySelector && (t.querySelector('input[name="selection"]') || t.querySelector('input[type="checkbox"]'))) {
|
if (t.querySelector && (t.querySelector('input[name="selection"]') || t.querySelector('input[type="checkbox"]'))) {
|
||||||
ensureControlsForTable(t);
|
ensureControlsForTable(t);
|
||||||
|
|||||||
Reference in New Issue
Block a user