.
This commit is contained in:
@@ -73,7 +73,6 @@ def create_question(exam):
|
|||||||
dir=settings.MEDIA_ROOT, suffix=".jpg", delete=False
|
dir=settings.MEDIA_ROOT, suffix=".jpg", delete=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
question: Question = Question.objects.create(
|
question: Question = Question.objects.create(
|
||||||
question_type=QuestionType.objects.first(),
|
question_type=QuestionType.objects.first(),
|
||||||
structure=Structure.objects.first(),
|
structure=Structure.objects.first(),
|
||||||
@@ -162,14 +161,13 @@ def test_exams(db, client):
|
|||||||
"eid": f"anatomy/{exam.pk}",
|
"eid": f"anatomy/{exam.pk}",
|
||||||
"cid": cid_user_1001.cid,
|
"cid": cid_user_1001.cid,
|
||||||
"start_time": "1659618141.731",
|
"start_time": "1659618141.731",
|
||||||
"answers": [[]]
|
"answers": [[]],
|
||||||
}
|
}
|
||||||
|
|
||||||
# Test submission
|
# Test submission
|
||||||
# Start with 0 answers
|
# Start with 0 answers
|
||||||
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
|
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
|
||||||
|
|
||||||
print(res.status_code)
|
|
||||||
json_res = json.loads(res.content)
|
json_res = json.loads(res.content)
|
||||||
|
|
||||||
assert json_res["success"] == True
|
assert json_res["success"] == True
|
||||||
@@ -179,10 +177,63 @@ def test_exams(db, client):
|
|||||||
"eid": f"anatomy/{exam.pk+10}",
|
"eid": f"anatomy/{exam.pk+10}",
|
||||||
"cid": cid_user_1001.cid,
|
"cid": cid_user_1001.cid,
|
||||||
"start_time": "1659618141.731",
|
"start_time": "1659618141.731",
|
||||||
"answers": [[]]
|
"answers": [[]],
|
||||||
}
|
}
|
||||||
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
|
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
|
||||||
json_res = json.loads(res.content)
|
json_res = json.loads(res.content)
|
||||||
assert json_res["success"] == False
|
assert json_res["success"] == False
|
||||||
|
|
||||||
# Test that we ca
|
# Test invalid cid
|
||||||
|
post_json = {
|
||||||
|
"eid": f"anatomy/{exam.pk+10}",
|
||||||
|
"cid": 1,
|
||||||
|
"start_time": "1659618141.731",
|
||||||
|
"answers": [[]],
|
||||||
|
}
|
||||||
|
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
|
||||||
|
json_res = json.loads(res.content)
|
||||||
|
assert json_res["success"] == False
|
||||||
|
assert json_res["error"] == "Invalid data"
|
||||||
|
|
||||||
|
valid_answers = []
|
||||||
|
|
||||||
|
for questions in exam_json["questions"]:
|
||||||
|
for qid in questions:
|
||||||
|
valid_answers.append(
|
||||||
|
{
|
||||||
|
"eid": exam_json["eid"],
|
||||||
|
"cid": cid_user_1001.cid,
|
||||||
|
"passcode": cid_user_1001.passcode,
|
||||||
|
"qid": qid,
|
||||||
|
"qidn": "1",
|
||||||
|
"ans": f"answer {qid}",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
post_json = {
|
||||||
|
"eid": exam_json["eid"],
|
||||||
|
"cid": cid_user_1001.cid,
|
||||||
|
"start_time": "1659618141.731",
|
||||||
|
"answers": json.dumps(valid_answers),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test submission
|
||||||
|
# give three answers
|
||||||
|
print("post_json", post_json)
|
||||||
|
res = client.post(reverse("global_exam_answers_submit"), data=post_json)
|
||||||
|
|
||||||
|
json_res = json.loads(res.content)
|
||||||
|
print(json_res)
|
||||||
|
|
||||||
|
assert json_res["success"] == True
|
||||||
|
assert json_res["question_count"] == 3
|
||||||
|
|
||||||
|
# We can then check the answers have been submitted (and can be viewed by the user)
|
||||||
|
cid_scores_res = client.get(
|
||||||
|
reverse(f"cid_scores", kwargs={"cid": 1001, "passcode": "EFGH"})
|
||||||
|
)
|
||||||
|
|
||||||
|
cid_scores_soup = BeautifulSoup(cid_scores_res.content, "html.parser")
|
||||||
|
assigned_exams = cid_scores_soup.find(id="exam-assigned").find_all("li")
|
||||||
|
print(assigned_exams)
|
||||||
|
|
||||||
|
|||||||
@@ -2470,3 +2470,7 @@ class ExamDeleteBase(RevisionMixin, DeleteView):
|
|||||||
def get_success_url(self) -> str:
|
def get_success_url(self) -> str:
|
||||||
# return super().get_success_url()(self):
|
# return super().get_success_url()(self):
|
||||||
return reverse_lazy(f"{self.model.app_name}:index")
|
return reverse_lazy(f"{self.model.app_name}:index")
|
||||||
|
|
||||||
|
|
||||||
|
def exam_inactive(request, context):
|
||||||
|
return render(request, "exam_inactive.html", context)
|
||||||
+8
-6
@@ -50,6 +50,7 @@ from generic.views import (
|
|||||||
ExamUpdateBase,
|
ExamUpdateBase,
|
||||||
ExamViews,
|
ExamViews,
|
||||||
GenericViewBase,
|
GenericViewBase,
|
||||||
|
exam_inactive,
|
||||||
)
|
)
|
||||||
|
|
||||||
from rest_framework import viewsets, permissions
|
from rest_framework import viewsets, permissions
|
||||||
@@ -168,7 +169,7 @@ def exam_take_old(request, pk):
|
|||||||
exam = get_object_or_404(Exam, pk=pk)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active:
|
if not exam.active:
|
||||||
raise Http404("Exam not found")
|
raise exam_inactive(request)
|
||||||
|
|
||||||
questions = exam.exam_questions.all()
|
questions = exam.exam_questions.all()
|
||||||
|
|
||||||
@@ -186,7 +187,7 @@ def exam_start(request, pk):
|
|||||||
exam = get_object_or_404(Exam, pk=pk)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active:
|
if not exam.active:
|
||||||
raise Http404("Exam not found")
|
return exam_inactive(request, context={"exam": exam})
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
@@ -200,12 +201,13 @@ def exam_start(request, pk):
|
|||||||
def exam_take_overview(request, pk, cid=None, passcode=None):
|
def exam_take_overview(request, pk, cid=None, passcode=None):
|
||||||
exam = get_object_or_404(Exam, pk=pk)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active:
|
|
||||||
raise Http404("Exam not found")
|
|
||||||
|
|
||||||
if cid is not None and not exam.check_cid_user(cid, passcode):
|
if cid is not None and not exam.check_cid_user(cid, passcode):
|
||||||
raise Http404("Error accessing exam")
|
raise Http404("Error accessing exam")
|
||||||
|
|
||||||
|
if not exam.active:
|
||||||
|
return exam_inactive(request, context={"exam": exam})
|
||||||
|
|
||||||
|
|
||||||
questions = exam.exam_questions.all()
|
questions = exam.exam_questions.all()
|
||||||
|
|
||||||
if cid is not None:
|
if cid is not None:
|
||||||
@@ -253,7 +255,7 @@ def exam_take(request, pk, sk, cid=None, passcode=None):
|
|||||||
exam = get_object_or_404(Exam, pk=pk)
|
exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
if not exam.active:
|
if not exam.active:
|
||||||
raise Http404("Exam not found")
|
return exam_inactive(request, context={"exam": exam})
|
||||||
|
|
||||||
if cid is not None and not exam.check_cid_user(cid, passcode):
|
if cid is not None and not exam.check_cid_user(cid, passcode):
|
||||||
raise Http404("Error accessing exam")
|
raise Http404("Error accessing exam")
|
||||||
|
|||||||
+1
-1
@@ -77,7 +77,7 @@ urlpatterns = [
|
|||||||
path("accounts/profile/<str:slug>/", views.account_profile, name="account_profile"),
|
path("accounts/profile/<str:slug>/", views.account_profile, name="account_profile"),
|
||||||
path("", TemplateView.as_view(template_name="index.html"), name="home"),
|
path("", TemplateView.as_view(template_name="index.html"), name="home"),
|
||||||
path("cid/results/<int:cid>/", views.cid_results, name="cid_results"),
|
path("cid/results/<int:cid>/", views.cid_results, name="cid_results"),
|
||||||
path("cid/<int:pk>/<str:passcode>", views.cid_scores, name="cid_scores"),
|
path("cid/<int:cid>/<str:passcode>", views.cid_scores, name="cid_scores"),
|
||||||
path("cid/<int:cid>/", views.cid_scores_admin, name="cid_scores_admin"),
|
path("cid/<int:cid>/", views.cid_scores_admin, name="cid_scores_admin"),
|
||||||
path("user/scores", views.user_scores, name="user_scores"),
|
path("user/scores", views.user_scores, name="user_scores"),
|
||||||
path("cid/", views.cid_selector, name="cid_selector"),
|
path("cid/", views.cid_selector, name="cid_selector"),
|
||||||
|
|||||||
+5
-6
@@ -186,11 +186,9 @@ def user_scores(request):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
def cid_scores(request, pk, passcode):
|
def cid_scores(request, cid, passcode):
|
||||||
# exam = get_object_or_404(Exam, pk=pk)
|
# exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
|
||||||
cid = pk
|
|
||||||
|
|
||||||
cid_user = CidUser.objects.filter(cid=cid).first()
|
cid_user = CidUser.objects.filter(cid=cid).first()
|
||||||
print(cid_user)
|
print(cid_user)
|
||||||
if not cid_user or cid_user.passcode != passcode:
|
if not cid_user or cid_user.passcode != passcode:
|
||||||
@@ -278,6 +276,7 @@ def active_exams_unbased(request, cid=None, passcode=None):
|
|||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def exam_submit(request):
|
def exam_submit(request):
|
||||||
|
print(request)
|
||||||
if request.is_ajax and request.method == "POST":
|
if request.is_ajax and request.method == "POST":
|
||||||
print(request.POST)
|
print(request.POST)
|
||||||
exam_type, exam_id = request.POST.get("eid").split("/")
|
exam_type, exam_id = request.POST.get("eid").split("/")
|
||||||
@@ -478,7 +477,7 @@ def view_feedback(request):
|
|||||||
|
|
||||||
# HTTP Error 400
|
# HTTP Error 400
|
||||||
def page_not_found(request, exception):
|
def page_not_found(request, exception):
|
||||||
response = render(request, "404.html", {})
|
response = render(request, "404.html")
|
||||||
|
|
||||||
response.status_code = 404
|
response.status_code = 404
|
||||||
|
|
||||||
@@ -486,7 +485,7 @@ def page_not_found(request, exception):
|
|||||||
|
|
||||||
|
|
||||||
def page_forbidden(request, exception):
|
def page_forbidden(request, exception):
|
||||||
response = render(request, "403.html", {})
|
response = render(request, "403.html")
|
||||||
|
|
||||||
response.status_code = 403
|
response.status_code = 403
|
||||||
|
|
||||||
@@ -494,7 +493,7 @@ def page_forbidden(request, exception):
|
|||||||
|
|
||||||
|
|
||||||
def server_error(request):
|
def server_error(request):
|
||||||
response = render(request, "500.html", {})
|
response = render(request, "500.html")
|
||||||
|
|
||||||
response.status_code = 500
|
response.status_code = 500
|
||||||
|
|
||||||
|
|||||||
@@ -280,6 +280,12 @@ button a {
|
|||||||
border: 1px dashed white;
|
border: 1px dashed white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.series-drop {
|
||||||
|
position: sticky;
|
||||||
|
height: 600px;
|
||||||
|
bottom: -500px;
|
||||||
|
}
|
||||||
|
|
||||||
.submit-button {
|
.submit-button {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
@@ -837,4 +843,19 @@ summary h5 {
|
|||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
opacity: 80%;
|
opacity: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.small-url-button {
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button a, button a:link, button a:visited, button a:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
details.filter {
|
||||||
|
border: 1px solid gray;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 20px;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
const data = document.currentScript.dataset;
|
||||||
|
const isDebug = data.debug === "True";
|
||||||
|
|
||||||
|
if (isDebug) {
|
||||||
|
document.addEventListener("htmx:beforeOnLoad", function (event) {
|
||||||
|
const xhr = event.detail.xhr;
|
||||||
|
if (xhr.status == 500 || xhr.status == 404) {
|
||||||
|
// Tell htmx to stop processing this response
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
document.children[0].innerHTML = xhr.response;
|
||||||
|
|
||||||
|
// Run Django’s inline script
|
||||||
|
// (1, eval) wtf - see https://stackoverflow.com/questions/9107240/1-evalthis-vs-evalthis-in-javascript
|
||||||
|
(1, eval)(document.scripts[0].innerText);
|
||||||
|
// Need to directly call Django’s onload function since browser won’t
|
||||||
|
window.onload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -579,4 +579,5 @@ function delete_multiple(url, csrf_token) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
window.delete_multiple = delete_multiple
|
window.delete_multiple = delete_multiple
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,373 @@
|
|||||||
|
let active_file_inputs = new Set();
|
||||||
|
|
||||||
|
function add_input_form() {
|
||||||
|
var form_idx = $('#id_images-TOTAL_FORMS').val();
|
||||||
|
$('#image_form_set').append($($('#empty_form').html().replace(/__prefix__/g, form_idx)).on("change",
|
||||||
|
input_change));
|
||||||
|
$('#id_images-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function blobToBase64(blob) {
|
||||||
|
return new Promise((resolve, _) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onloadend = () => resolve(reader.result);
|
||||||
|
reader.readAsDataURL(blob);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function extendInputs(form_id, n) {
|
||||||
|
// Makes sure we have n inputs available
|
||||||
|
// returns available inputs
|
||||||
|
inputs = getUnusedInputs($(`#${form_id} input[type=file][id^=id_images-]`));
|
||||||
|
//fileInput = document.getElementById("id_images-0-image");
|
||||||
|
|
||||||
|
|
||||||
|
// Make sure we have enough input targets
|
||||||
|
input_diff = (n - inputs.length)
|
||||||
|
|
||||||
|
if (input_diff > 0) {
|
||||||
|
for (let j = 0; j < input_diff; j++) {
|
||||||
|
add_input_form()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to make sure we include the new ones...
|
||||||
|
inputs = getUnusedInputs($(`#${form_id} input[type=file][id^=id_images-]`));
|
||||||
|
}
|
||||||
|
return inputs;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentFormFiles(form_id) {
|
||||||
|
inputs = $(`#${form_id} input[type=file]`);
|
||||||
|
|
||||||
|
let files = new Set();
|
||||||
|
for (let j = 0; j < inputs.length; j++) {
|
||||||
|
i = inputs.get(j);
|
||||||
|
if (i.files.length > 0) {
|
||||||
|
files.add(i.files[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUnusedInputs(inputs) {
|
||||||
|
new_inputs = [];
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
input = inputs.get(i);
|
||||||
|
|
||||||
|
if (!input.value) {
|
||||||
|
new_inputs.push(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return $(new_inputs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function addFile(f, feedback) {
|
||||||
|
let dT = new DataTransfer();
|
||||||
|
dT.clearData();
|
||||||
|
dT.items.add(f);
|
||||||
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
|
el = inputs.get(i);
|
||||||
|
|
||||||
|
if (el.files.length == 0) {
|
||||||
|
el.files = dT.files;
|
||||||
|
//ocr(el)
|
||||||
|
|
||||||
|
if (feedback) {
|
||||||
|
arr = el.name.split("-");
|
||||||
|
arr.splice(2, 1, "feedback_image");
|
||||||
|
feedback_el_name = arr.join("-");
|
||||||
|
|
||||||
|
$(`[name=${feedback_el_name}]`).prop("checked", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$(el).change();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function ocr2(url, input) {
|
||||||
|
console.log("OCR", input)
|
||||||
|
Tesseract.recognize(
|
||||||
|
url,
|
||||||
|
'eng',
|
||||||
|
//{ logger: m => console.log(m) }
|
||||||
|
).then(({
|
||||||
|
data: {
|
||||||
|
text
|
||||||
|
}
|
||||||
|
}) => {
|
||||||
|
l = text.toLowerCase();
|
||||||
|
uploading_el = $(`img[data-input-id='${input.id}'], div[data-input-id='${input.id}']`)
|
||||||
|
uploading_el.removeClass("image-ident-loading");
|
||||||
|
console.log("found text", l);
|
||||||
|
if (l.includes("accesion") || l.includes("number") || l.search(
|
||||||
|
/(ref|rk9|rh8|ra9|rbz)\d+/g) > -1) {
|
||||||
|
console.log("Match found ", input);
|
||||||
|
$(input).addClass("image-ident-warning");
|
||||||
|
uploading_el.addClass("image-ident-warning");
|
||||||
|
} else {
|
||||||
|
$(input).addClass("image-ident-ok");
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function input_change(evt) {
|
||||||
|
file = evt.target.files[0];
|
||||||
|
$(evt.target).removeClass("image-ident-warning");
|
||||||
|
$(evt.target).removeClass("image-ident-ok");
|
||||||
|
|
||||||
|
el = evt.target;
|
||||||
|
|
||||||
|
readFileAndProcess2(el);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function showEditPopup(url) {
|
||||||
|
var win = window.open(url, "Edit",
|
||||||
|
'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showAddPopup(triggeringLink) {
|
||||||
|
var name = triggeringLink.id.replace(/^add_/, '');
|
||||||
|
href = triggeringLink.href;
|
||||||
|
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
|
||||||
|
win.focus();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function closePopup(win, newID, newRepr, id) {
|
||||||
|
$(id + "_to").append('<option value=' + newID + ' title=' + newRepr + ' >' + newRepr + '</option>')
|
||||||
|
win.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async function readFileAndProcess2(el) {
|
||||||
|
|
||||||
|
//file_set = $("#image_form_set input[type=file]");
|
||||||
|
|
||||||
|
//file_set.each(async (n, el) => {
|
||||||
|
|
||||||
|
if (el.files.length > 0) {
|
||||||
|
filename = el.files[0].name;
|
||||||
|
// Probably no need to await here anymore
|
||||||
|
//await readFile(el.files[0], el);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$(el).parent().parent().find(".temp-thumb").remove();
|
||||||
|
|
||||||
|
let url = URL.createObjectURL(el.files[0])
|
||||||
|
|
||||||
|
n = el.id.split("-")[1];
|
||||||
|
$(`#drop-filenames span[data-input-id='${el.id}']`).remove();
|
||||||
|
|
||||||
|
let extra_class = " image-ident-loading";
|
||||||
|
if ($(el).hasClass("image-ident-warning")) {
|
||||||
|
extra_class = " image-ident-warning";
|
||||||
|
} else if ($(el).hasClass("image-ident-ok")) {
|
||||||
|
extra_class = " image-ident-ok";
|
||||||
|
}
|
||||||
|
|
||||||
|
//let extra_class = "";
|
||||||
|
let imageId;
|
||||||
|
if (el.files[0].type.startsWith("image")) {
|
||||||
|
|
||||||
|
let hash = CryptoJS.MD5(el.files[0]).toString();
|
||||||
|
|
||||||
|
base = await blobToBase64(el.files[0]);
|
||||||
|
imageId = "base64://" + base.split(",")[1];
|
||||||
|
imageId2 = base;
|
||||||
|
const element = $(`<img class='temp-thumb' src=${imageId}></div>`).get(0)
|
||||||
|
$(el).parent().parent().prepend(element);
|
||||||
|
|
||||||
|
|
||||||
|
ocr2(url, el);
|
||||||
|
checkHash(hash, el);
|
||||||
|
|
||||||
|
if ($("#drop-filenames").length > 0) {
|
||||||
|
$("#drop-filenames").append(
|
||||||
|
`<span data-input-id='${el.id}'><span>${n}: ${el.files[0].name}</span><img class='uploading${extra_class}' data-input-id='${el.id}' src=${url}></span>`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageId = `wadouri:${url}`;
|
||||||
|
imageId2 = imageId
|
||||||
|
|
||||||
|
const element = $(`<div class='temp-thumb' src=${imageId2}></div>`).get(0)
|
||||||
|
$(el).parent().parent().prepend(element);
|
||||||
|
cornerstone.enable(element);
|
||||||
|
cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
||||||
|
cornerstone.displayImage(element, image);
|
||||||
|
cornerstone.resize(element)
|
||||||
|
|
||||||
|
|
||||||
|
if (hash_url) {
|
||||||
|
let pixel_data = image.getPixelData().toString()
|
||||||
|
|
||||||
|
let hash = CryptoJS.MD5(pixel_data).toString();
|
||||||
|
checkHash(hash, el);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log("image", image)
|
||||||
|
|
||||||
|
extractDicomDetails(image.data.byteArray)
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if ($("#drop-filenames").length > 0) {
|
||||||
|
$("#drop-filenames").append(
|
||||||
|
`<span data-input-id='${el.id}'><span>${n}: ${el.files[0].name}</span><div class='uploading${extra_class}' data-input-id='${el.id}' src=${url}></div></span>`
|
||||||
|
)
|
||||||
|
|
||||||
|
const element2 = $(`#drop-filenames div[data-input-id='${el.id}']`).get(0);
|
||||||
|
cornerstone.enable(element2);
|
||||||
|
cornerstone.loadAndCacheImage(imageId).then(function (image) {
|
||||||
|
cornerstone.displayImage(element2, image);
|
||||||
|
|
||||||
|
$(element2).addClass("temp-thumb-large");
|
||||||
|
cornerstone.resize(element2)
|
||||||
|
setTimeout(function () {
|
||||||
|
ocr2($(element2).find("canvas").get(0).toDataURL(), el);
|
||||||
|
$(element2).removeClass("temp-thumb-large");
|
||||||
|
cornerstone.resize(element2)
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
active_file_inputs.delete(el)
|
||||||
|
console.log("active", active_file_inputs)
|
||||||
|
// Only load once all queued files have been processed
|
||||||
|
if (active_file_inputs.size < 1) {
|
||||||
|
//load_viewer = true;
|
||||||
|
loadViewer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//})
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkHash(hash, input, url) {
|
||||||
|
$.ajax({
|
||||||
|
url: hash_url,
|
||||||
|
data: {
|
||||||
|
csrfmiddlewaretoken: csrf_token,
|
||||||
|
hash: hash
|
||||||
|
},
|
||||||
|
type: "POST",
|
||||||
|
dataType: "json",
|
||||||
|
})
|
||||||
|
// $.ajax().done(), $.ajax().fail(), $ajax().always() are upto you. Add/change accordingly
|
||||||
|
.done(function (data) {
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
if (data.status == "success") {
|
||||||
|
console.log("success");
|
||||||
|
console.log(data.id)
|
||||||
|
if (data.id) {
|
||||||
|
//toastr.info(`id ${data.id}`)
|
||||||
|
uploading_el = $(`img[data-input-id='${input.id}'], div[data-input-id='${input.id}']`)
|
||||||
|
$(input).addClass("image-duplicate");
|
||||||
|
uploading_el.addClass("image-duplicate");
|
||||||
|
|
||||||
|
toastr.warning(`Image (${input.files[0].name}) already exists.<br /><a href='${data.url}'>Click to view</a><br /><button type="button" class="btn clear">Dismiss</button>`, "Duplicate image", {
|
||||||
|
"closeButton": false,
|
||||||
|
"debug": false,
|
||||||
|
"newestOnTop": false,
|
||||||
|
"progressBar": false,
|
||||||
|
"positionClass": "toast-top-full-width",
|
||||||
|
"preventDuplicates": false,
|
||||||
|
"onclick": null,
|
||||||
|
"showDuration": "0",
|
||||||
|
"hideDuration": "0",
|
||||||
|
"timeOut": 0,
|
||||||
|
"extendedTimeOut": 0,
|
||||||
|
"showEasing": "swing",
|
||||||
|
"hideEasing": "linear",
|
||||||
|
"showMethod": "fadeIn",
|
||||||
|
"hideMethod": "fadeOut",
|
||||||
|
"tapToDismiss": false
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.always(function () {
|
||||||
|
console.log('[Done]');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function updateImagePositions() {
|
||||||
|
console.log("UP pos");
|
||||||
|
|
||||||
|
file_set = $("#image_form_set ul");
|
||||||
|
|
||||||
|
n = 0;
|
||||||
|
for (let i = 0; i < file_set.length; i++) {
|
||||||
|
ul = file_set.get(i);
|
||||||
|
console.log(ul)
|
||||||
|
|
||||||
|
file_element = $(ul).find("input[type=file]").get(0);
|
||||||
|
|
||||||
|
// If a (new) file is being uploaded we save it's name prior to it being hashed
|
||||||
|
if (file_element.files.length > 0) {
|
||||||
|
file_name = file_element.files[0].name
|
||||||
|
//$(ul).find("input[type=text]").val(file_name);
|
||||||
|
$(ul).find("input[name*='filename']").val(file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_element.files.length > 0 || $(ul).find(":contains('Currently:')")) {
|
||||||
|
n++;
|
||||||
|
$(ul).find("input[type=number]").val(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function loadViewer() {
|
||||||
|
console.log("LOAD VIEWER")
|
||||||
|
// temp fix before starting viewer collapsed
|
||||||
|
$("#single-dicom-viewer").show()
|
||||||
|
//file_set = $("#image_form_set input[type=file]");
|
||||||
|
|
||||||
|
//n = 0;
|
||||||
|
//for (let i = 0; i < file_set.length; i++) {
|
||||||
|
// el = file_set.get(i)
|
||||||
|
// if (el.files.length > 0) {
|
||||||
|
// n++;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
image_set = $("#image_form_set .temp-thumb").get() //.map(function() { return $(this).attr("src")}).get();
|
||||||
|
console.log(image_set);
|
||||||
|
//console.log()
|
||||||
|
//if (n == image_set.length) {
|
||||||
|
const event = new CustomEvent('loadDicomViewer', {
|
||||||
|
"detail": image_set
|
||||||
|
});
|
||||||
|
|
||||||
|
window.dispatchEvent(event);
|
||||||
|
sortable('.sortable');
|
||||||
|
//sortable('.sortable')[0].addEventListener('sortstop', function(e) {
|
||||||
|
updateImagePositions();
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
+3
-1
@@ -3,6 +3,8 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="">
|
<div class="">
|
||||||
<h2>404 error</h2>
|
<h2>404 error</h2>
|
||||||
Page not found....
|
{{ reason }}
|
||||||
|
|
||||||
|
<h3>a {{resolved}}</h3>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
+29
-25
@@ -4,43 +4,47 @@
|
|||||||
<div class="">
|
<div class="">
|
||||||
<h2>CID: {{ cid }}</h2>
|
<h2>CID: {{ cid }}</h2>
|
||||||
<h3>Assigned exams</h3>
|
<h3>Assigned exams</h3>
|
||||||
<p>The following exams will be available to take when active.
|
<div id="exam-assigned">
|
||||||
|
The following exams will be available to take when active.
|
||||||
{% for exam_type, exams in available_exams %}
|
{% for exam_type, exams in available_exams %}
|
||||||
<h4>{{exam_type}}</h4>
|
<h4>{{exam_type}}</h4>
|
||||||
<ul>
|
<ul class='{{exam_type}}'>
|
||||||
{% for exam in exams %}
|
{% for exam in exams %}
|
||||||
<li><a href="{{exam.get_take_url}}?cid={{cid}}&passcode={{passcode}}" target="_blank">{{exam.name}} {% if exam.active %}[Active]{% endif %}</a></li>
|
<li class="exam"><a href="{{exam.get_take_url}}?cid={{cid}}&passcode={{passcode}}" target="_blank">{{exam.name}} {% if exam.active %}[Active]{% endif %}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if all_exams %}
|
{% if all_exams %}
|
||||||
<h3>Exam results</h3>
|
|
||||||
<div>
|
|
||||||
The following exam results been found. Click to view answers (and scores when the results are published):
|
|
||||||
{% for exam_type, exams in all_exams %}
|
|
||||||
{% if exams %}
|
|
||||||
<h4>{{exam_type|title}}</h4>
|
|
||||||
<ul>
|
|
||||||
{% for exam in exams %}
|
|
||||||
<li><a href="{% url exam_type|add:':exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}">{{exam.name}}</a> {% if exam.active %}[Active]{% endif %} {% if exam.publish_results %}[Results Published]{% endif %}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
<h3>Exam results</h3>
|
||||||
|
<div id="exam-results">
|
||||||
|
The following exam results been found. Click to view answers (and scores when the results are published):
|
||||||
|
{% for exam_type, exams in all_exams %}
|
||||||
|
{% if exams %}
|
||||||
|
<h4>{{exam_type|title}}</h4>
|
||||||
|
<ul class='{{exam_type}}'>
|
||||||
|
{% for exam in exams %}
|
||||||
|
<li class="exam"><a href="{% url exam_type|add:':exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}">{{exam.name}}</a> {% if exam.active %}[Active]{% endif %} {% if exam.publish_results %}[Results Published]{% endif %}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if case_collections %}
|
{% if case_collections %}
|
||||||
<div>
|
<div>
|
||||||
<h3>Case Collection</h3>
|
<h3>Case Collection</h3>
|
||||||
The following Case Collections have been found.
|
The following Case Collections have been found.
|
||||||
{% for collection in case_collections %}
|
<ul>
|
||||||
<li><a href="{{collection.get_take_url}}?cid={{cid}}&passcode={{passcode}}">{{collection.name}}</a> {% if collection.active %}[Active]{% endif %} {% if collection.publish_results %}[Results Published]{% endif %}</li>
|
{% for collection in case_collections %}
|
||||||
|
<li><a href="{{collection.get_take_url}}?cid={{cid}}&passcode={{passcode}}">{{collection.name}}</a> {% if collection.active %}[Active]{% endif %} {% if collection.publish_results %}[Results Published]{% endif %}</li>
|
||||||
{% endfor %}
|
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="">
|
||||||
|
<h2>{{exam.name}}</h2>
|
||||||
|
Exam is currently inactive.
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -13,4 +13,6 @@
|
|||||||
{% if request.user.is_staff %}
|
{% if request.user.is_staff %}
|
||||||
<p><a href="{% url 'password_change' %}">Change password</a></p>
|
<p><a href="{% url 'password_change' %}">Change password</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<p>This page is for user account logins. If you have a CID / Passcode <a href="{% url 'cid_selector' %}">click here</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user