Enhance import preview functionality with editable fields and overlay logic for payload updates

This commit is contained in:
Ross
2025-10-20 22:35:01 +01:00
parent 8dac99b6d3
commit 45e50e9e93
2 changed files with 181 additions and 136 deletions
@@ -2,9 +2,14 @@
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="session_key" value="{{ session_key }}"> <input type="hidden" name="session_key" value="{{ session_key }}">
{% for item in items %} {% for item in items %}
<div class="card mb-2"> <div class="card mb-2" id="import-item-{{ item.index }}">
<div class="card-body"> <div class="card-body">
<h5 class="card-title">Question {{ item.index|add:1 }}</h5> <div class="d-flex justify-content-between align-items-start">
<h5 class="card-title mb-0">Question {{ item.index|add:1 }}</h5>
<div>
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" data-item-index="{{ item.index }}" onclick="toggleEdit(this)">Edit</button>
</div>
</div>
{% if item.errors %} {% if item.errors %}
<div class="alert alert-danger" role="alert"> <div class="alert alert-danger" role="alert">
<strong>Validation errors:</strong> <strong>Validation errors:</strong>
@@ -25,48 +30,55 @@
<strong>Note:</strong> Numeric references were removed from fields: <em>{{ item.stripped_fields|join:", " }}</em> <strong>Note:</strong> Numeric references were removed from fields: <em>{{ item.stripped_fields|join:", " }}</em>
</div> </div>
{% endif %} {% endif %}
{% if item.payload %} <div class="mb-2">
<p><strong>Title:</strong> {{ item.payload.title|default:'(No title)'|safe }}</p> <label class="form-label"><strong>Title</strong></label>
<p><strong>Stem:</strong> {{ item.payload.stem|default:''|safe }}</p> <input class="form-control form-control-sm" name="payload_{{ item.index }}_title" value="{{ item.payload.title|default_if_none:'' }}" disabled>
<p><strong>Answers:</strong> </div>
<ul> <div class="mb-2">
<li>A: {{ item.payload.a_answer|default:''|safe }}</li> <label class="form-label"><strong>Stem</strong></label>
{% if item.payload.a_feedback %} <textarea class="form-control form-control-sm" name="payload_{{ item.index }}_stem" rows="3" disabled>{{ item.payload.stem|default_if_none:'' }}</textarea>
<p><strong>A Feedback:</strong> {{ item.payload.a_feedback|default:''|safe }}</p> </div>
{% endif %} <div class="mb-2">
<li>B: {{ item.payload.b_answer|default:''|safe }}</li> <label class="form-label"><strong>Answers</strong></label>
{% if item.payload.b_feedback %} <div class="mb-1">
<p><strong>B Feedback:</strong> {{ item.payload.b_feedback|default:''|safe }}</p> <label class="form-label small">A</label>
{% endif %} <input class="form-control form-control-sm" name="payload_{{ item.index }}_a_answer" value="{{ item.payload.a_answer|default_if_none:'' }}" disabled>
<li>C: {{ item.payload.c_answer|default:''|safe }}</li> <label class="form-label small mt-1">A Feedback</label>
{% if item.payload.c_feedback %} <textarea class="form-control form-control-sm" name="payload_{{ item.index }}_a_feedback" rows="2" disabled>{{ item.payload.a_feedback|default_if_none:'' }}</textarea>
<p><strong>C Feedback:</strong> {{ item.payload.c_feedback|default:''|safe }}</p> </div>
{% endif %} <div class="mb-1">
<li>D: {{ item.payload.d_answer|default:''|safe }}</li> <label class="form-label small">B</label>
{% if item.payload.d_feedback %} <input class="form-control form-control-sm" name="payload_{{ item.index }}_b_answer" value="{{ item.payload.b_answer|default_if_none:'' }}" disabled>
<p><strong>D Feedback:</strong> {{ item.payload.d_feedback|default:''|safe }}</p> <label class="form-label small mt-1">B Feedback</label>
{% endif %} <textarea class="form-control form-control-sm" name="payload_{{ item.index }}_b_feedback" rows="2" disabled>{{ item.payload.b_feedback|default_if_none:'' }}</textarea>
<li>E: {{ item.payload.e_answer|default:''|safe }}</li> </div>
{% if item.payload.e_feedback %} <div class="mb-1">
<p><strong>E Feedback:</strong> {{ item.payload.e_feedback|default:''|safe }}</p> <label class="form-label small">C</label>
{% endif %} <input class="form-control form-control-sm" name="payload_{{ item.index }}_c_answer" value="{{ item.payload.c_answer|default_if_none:'' }}" disabled>
<label class="form-label small mt-1">C Feedback</label>
</ul> <textarea class="form-control form-control-sm" name="payload_{{ item.index }}_c_feedback" rows="2" disabled>{{ item.payload.c_feedback|default_if_none:'' }}</textarea>
<p><strong>Best Answer:</strong> {{ item.payload.best_answer|default:'' }} </div>
</p> <div class="mb-1">
<p><strong>Feedback:</strong> {{ item.payload.feedback|default:''|safe }}</p> <label class="form-label small">D</label>
<p><strong>Sources:</strong> <input class="form-control form-control-sm" name="payload_{{ item.index }}_d_answer" value="{{ item.payload.d_answer|default_if_none:'' }}" disabled>
{% if item.payload.sources %} <label class="form-label small mt-1">D Feedback</label>
<ul> <textarea class="form-control form-control-sm" name="payload_{{ item.index }}_d_feedback" rows="2" disabled>{{ item.payload.d_feedback|default_if_none:'' }}</textarea>
{% for source in item.payload.sources %} </div>
<li>{{ source }}</li> <div class="mb-1">
{% endfor %} <label class="form-label small">E</label>
</ul> <input class="form-control form-control-sm" name="payload_{{ item.index }}_e_answer" value="{{ item.payload.e_answer|default_if_none:'' }}" disabled>
{% else %} <label class="form-label small mt-1">E Feedback</label>
None <textarea class="form-control form-control-sm" name="payload_{{ item.index }}_e_feedback" rows="2" disabled>{{ item.payload.e_feedback|default_if_none:'' }}</textarea>
{% endif %} </div>
</p> <div class="mb-1">
{% endif %} <label class="form-label small">Best Answer</label>
<input class="form-control form-control-sm" name="payload_{{ item.index }}_best_answer" value="{{ item.payload.best_answer|default_if_none:'' }}" disabled>
</div>
<div class="mb-1">
<label class="form-label small">Feedback</label>
<textarea class="form-control form-control-sm" name="payload_{{ item.index }}_feedback" rows="2" disabled>{{ item.payload.feedback|default_if_none:'' }}</textarea>
</div>
<div class="mb-1 small text-muted">Sources: {% if item.payload.sources %}{{ item.payload.sources|join:", " }}{% else %}None{% endif %}</div>
<p><strong>M2M fields (click to exclude)</strong></p> <p><strong>M2M fields (click to exclude)</strong></p>
<p class="small text-muted">Green = will be created/attached. Click a value to exclude it (grey).</p> <p class="small text-muted">Green = will be created/attached. Click a value to exclude it (grey).</p>
<div class="mb-2"> <div class="mb-2">
@@ -255,3 +267,22 @@ missing_map: {{ item.missing_map|default:"{}" }}</pre>
</script> </script>
<!-- Bottom controls removed: submit and cancel are inside the form above --> <!-- Bottom controls removed: submit and cancel are inside the form above -->
<script>
// Toggle editing of payload inputs for a specific item (disabled by default)
function toggleEdit(btn){
const idx = btn.getAttribute('data-item-index');
if(!idx) return;
const card = document.getElementById('import-item-' + idx);
if(!card) return;
const selector = 'input[name^="payload_' + idx + '_"], textarea[name^="payload_' + idx + '_"]';
const inputs = card.querySelectorAll(selector);
if(inputs.length === 0) return;
// check current state from first input
const currentlyDisabled = inputs[0].disabled === true;
// toggle
inputs.forEach(function(i){ i.disabled = currentlyDisabled ? false : true; });
// update button text
btn.textContent = currentlyDisabled ? 'Lock' : 'Edit';
}
</script>
+14
View File
@@ -950,6 +950,20 @@ def import_llm_confirm(request):
except Exception: except Exception:
errors.append({"index": idx, "errors": ["Missing candidate"]}) errors.append({"index": idx, "errors": ["Missing candidate"]})
continue continue
# Overlay any edited fields posted from the preview form (names like payload_<idx>_stem)
overlay = {}
prefix = f"payload_{idx}_"
for k, v in request.POST.items():
if k.startswith(prefix):
field = k[len(prefix):]
overlay[field] = v
if overlay:
# apply overlay onto a shallow copy
new_payload = dict(payload)
for fk, fv in overlay.items():
# convert list-like fields and numeric conversions are not performed here; keep simple strings/lists
new_payload[fk] = fv
payload = new_payload
# Strip numeric bracket references here as well, before confirm import # Strip numeric bracket references here as well, before confirm import
payload, stripped = _strip_numeric_refs_in_payload(payload) payload, stripped = _strip_numeric_refs_in_payload(payload)
if stripped: if stripped: