forked from enviPath/enviPy
[Feature] Show Multi Gen Eval + Batch Prediction (#267)
Co-authored-by: Tim Lorsbach <tim@lorsba.ch> Reviewed-on: enviPath/enviPy#267
This commit is contained in:
@ -3,7 +3,9 @@
|
||||
{% block content %}
|
||||
|
||||
{% block action_modals %}
|
||||
{# {% include "modals/objects/refresh_job_log.html" %}#}
|
||||
{% if job.is_result_downloadable %}
|
||||
{% include "modals/objects/download_job_result_modal.html" %}
|
||||
{% endif %}
|
||||
{% endblock action_modals %}
|
||||
|
||||
<div class="space-y-2 p-4">
|
||||
@ -49,22 +51,20 @@
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Description</div>
|
||||
<div class="collapse-content">
|
||||
Status page for Task {{ job.job_name }}
|
||||
</div>
|
||||
<div class="collapse-content">Status page for Job {{ job.job_name }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Job Status -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Task Status</div>
|
||||
<div class="collapse-title text-xl font-medium">Job Status</div>
|
||||
<div class="collapse-content">{{ job.status }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Job ID -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Task ID</div>
|
||||
<div class="collapse-title text-xl font-medium">Job ID</div>
|
||||
<div class="collapse-content">{{ job.task_id }}</div>
|
||||
</div>
|
||||
|
||||
@ -72,7 +72,7 @@
|
||||
{% if job.is_in_terminal_state %}
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Task Result</div>
|
||||
<div class="collapse-title text-xl font-medium">Job Result</div>
|
||||
<div class="collapse-content">
|
||||
{% if job.job_name == 'engineer_pathways' %}
|
||||
<div class="card bg-base-100">
|
||||
@ -103,6 +103,68 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% elif job.job_name == 'batch_predict' %}
|
||||
<div
|
||||
id="table-container"
|
||||
class="overflow-x-auto overflow-y-auto max-h-96 border rounded-lg"
|
||||
></div>
|
||||
|
||||
<script>
|
||||
const input = `{{ job.task_result }}`;
|
||||
|
||||
function renderCsvTable(str) {
|
||||
const lines = str
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter(Boolean);
|
||||
const [headerLine, ...rows] = lines;
|
||||
|
||||
const headers = headerLine.split(",").map((h) => h.trim());
|
||||
|
||||
const table = document.createElement("table");
|
||||
table.className = "table table-zebra w-full";
|
||||
|
||||
const thead = document.createElement("thead");
|
||||
const headerRow = document.createElement("tr");
|
||||
|
||||
headers.forEach((h) => {
|
||||
const th = document.createElement("th");
|
||||
th.textContent = h;
|
||||
headerRow.appendChild(th);
|
||||
});
|
||||
|
||||
thead.appendChild(headerRow);
|
||||
|
||||
const tbody = document.createElement("tbody");
|
||||
|
||||
rows.forEach((rowStr) => {
|
||||
console.log(rowStr.split(","));
|
||||
console.log(headers);
|
||||
const row = document.createElement("tr");
|
||||
const cells = rowStr.split(",").map((c) => c.trim());
|
||||
|
||||
headers.forEach((_, i) => {
|
||||
const td = document.createElement("td");
|
||||
|
||||
const value = cells[i] || "";
|
||||
|
||||
td.textContent = value;
|
||||
|
||||
row.appendChild(td);
|
||||
});
|
||||
console.log(row);
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
|
||||
table.appendChild(thead);
|
||||
table.appendChild(tbody);
|
||||
return table;
|
||||
}
|
||||
|
||||
document
|
||||
.getElementById("table-container")
|
||||
.appendChild(renderCsvTable(input));
|
||||
</script>
|
||||
{% else %}
|
||||
{{ job.parsed_result }}
|
||||
{% endif %}
|
||||
|
||||
@ -73,13 +73,29 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Reaction Packages -->
|
||||
{% endif %}
|
||||
<!-- Reaction Packages -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Reaction Packages</div>
|
||||
<div class="collapse-content">
|
||||
<ul class="menu bg-base-100 rounded-box w-full">
|
||||
{% for p in model.data_packages.all %}
|
||||
<li>
|
||||
<a href="{{ p.url }}" class="hover:bg-base-200">{{ p.name }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% if model.eval_packages.all|length > 0 %}
|
||||
<!-- Eval Packages -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Reaction Packages</div>
|
||||
<div class="collapse-title text-xl font-medium">Eval Packages</div>
|
||||
<div class="collapse-content">
|
||||
<ul class="menu bg-base-100 rounded-box w-full">
|
||||
{% for p in model.data_packages.all %}
|
||||
{% for p in model.eval_packages.all %}
|
||||
<li>
|
||||
<a href="{{ p.url }}" class="hover:bg-base-200">{{ p.name }}</a>
|
||||
</li>
|
||||
@ -87,31 +103,13 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% if model.eval_packages.all|length > 0 %}
|
||||
<!-- Eval Packages -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Eval Packages</div>
|
||||
<div class="collapse-content">
|
||||
<ul class="menu bg-base-100 rounded-box w-full">
|
||||
{% for p in model.eval_packages.all %}
|
||||
<li>
|
||||
<a href="{{ p.url }}" class="hover:bg-base-200"
|
||||
>{{ p.name }}</a
|
||||
>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Model Status -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Model Status</div>
|
||||
<div class="collapse-content">{{ model.status }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Model Status -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">Model Status</div>
|
||||
<div class="collapse-content">{{ model.status }}</div>
|
||||
</div>
|
||||
|
||||
{% if model.ready_for_prediction %}
|
||||
<!-- Predict Panel -->
|
||||
@ -174,7 +172,6 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if model.model_status == 'FINISHED' %}
|
||||
<!-- Single Gen Curve Panel -->
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
@ -188,6 +185,19 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if model.multigen_eval %}
|
||||
<div class="collapse-arrow bg-base-200 collapse">
|
||||
<input type="checkbox" checked />
|
||||
<div class="collapse-title text-xl font-medium">
|
||||
Multi Gen Precision Recall Curve
|
||||
</div>
|
||||
<div class="collapse-content">
|
||||
<div class="flex justify-center">
|
||||
<div id="mg-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@ -244,6 +254,105 @@
|
||||
}
|
||||
}
|
||||
|
||||
function makeChart(selector, data) {
|
||||
const x = ['Recall'];
|
||||
const y = ['Precision'];
|
||||
const thres = ['threshold'];
|
||||
|
||||
function compare(a, b) {
|
||||
if (a.threshold < b.threshold)
|
||||
return -1;
|
||||
else if (a.threshold > b.threshold)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getIndexForValue(data, val, val_name) {
|
||||
for (const idx in data) {
|
||||
if (data[idx][val_name] == val) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
console.warn('PR curve data is empty');
|
||||
return;
|
||||
}
|
||||
const dataLength = data.length;
|
||||
data.sort(compare);
|
||||
|
||||
for (const idx in data) {
|
||||
const d = data[idx];
|
||||
x.push(d.recall);
|
||||
y.push(d.precision);
|
||||
thres.push(d.threshold);
|
||||
}
|
||||
const chart = c3.generate({
|
||||
bindto: selector,
|
||||
data: {
|
||||
onclick: function (d, e) {
|
||||
const idx = d.index;
|
||||
const thresh = data[dataLength - idx - 1].threshold;
|
||||
},
|
||||
x: 'Recall',
|
||||
y: 'Precision',
|
||||
columns: [
|
||||
x,
|
||||
y,
|
||||
]
|
||||
},
|
||||
size: {
|
||||
height: 400,
|
||||
width: 480
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
max: 1,
|
||||
min: 0,
|
||||
label: 'Recall',
|
||||
padding: 0,
|
||||
tick: {
|
||||
fit: true,
|
||||
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
||||
}
|
||||
},
|
||||
y: {
|
||||
max: 1,
|
||||
min: 0,
|
||||
label: 'Precision',
|
||||
padding: 0,
|
||||
tick: {
|
||||
fit: true,
|
||||
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
||||
}
|
||||
}
|
||||
},
|
||||
point: {
|
||||
r: 4
|
||||
},
|
||||
tooltip: {
|
||||
format: {
|
||||
title: function (recall) {
|
||||
const idx = getIndexForValue(data, recall, "recall");
|
||||
if (idx != -1) {
|
||||
return "Threshold: " + data[idx].threshold;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
value: function (precision, ratio, id) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
zoom: {
|
||||
enabled: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function makeLoadingGif(selector, gifPath) {
|
||||
const element = document.querySelector(selector);
|
||||
if (element) {
|
||||
@ -260,107 +369,12 @@
|
||||
}
|
||||
|
||||
{% if model.model_status == 'FINISHED' %}
|
||||
// Precision Recall Curve
|
||||
const sgChart = document.getElementById('sg-chart');
|
||||
if (sgChart) {
|
||||
const x = ['Recall'];
|
||||
const y = ['Precision'];
|
||||
const thres = ['threshold'];
|
||||
|
||||
function compare(a, b) {
|
||||
if (a.threshold < b.threshold)
|
||||
return -1;
|
||||
else if (a.threshold > b.threshold)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getIndexForValue(data, val, val_name) {
|
||||
for (const idx in data) {
|
||||
if (data[idx][val_name] == val) {
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
var data = {{ model.pr_curve|safe }};
|
||||
if (!data || data.length === 0) {
|
||||
console.warn('PR curve data is empty');
|
||||
return;
|
||||
}
|
||||
const dataLength = data.length;
|
||||
data.sort(compare);
|
||||
|
||||
for (const idx in data) {
|
||||
const d = data[idx];
|
||||
x.push(d.recall);
|
||||
y.push(d.precision);
|
||||
thres.push(d.threshold);
|
||||
}
|
||||
const chart = c3.generate({
|
||||
bindto: '#sg-chart',
|
||||
data: {
|
||||
onclick: function (d, e) {
|
||||
const idx = d.index;
|
||||
const thresh = data[dataLength - idx - 1].threshold;
|
||||
},
|
||||
x: 'Recall',
|
||||
y: 'Precision',
|
||||
columns: [
|
||||
x,
|
||||
y,
|
||||
]
|
||||
},
|
||||
size: {
|
||||
height: 400,
|
||||
width: 480
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
max: 1,
|
||||
min: 0,
|
||||
label: 'Recall',
|
||||
padding: 0,
|
||||
tick: {
|
||||
fit: true,
|
||||
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
||||
}
|
||||
},
|
||||
y: {
|
||||
max: 1,
|
||||
min: 0,
|
||||
label: 'Precision',
|
||||
padding: 0,
|
||||
tick: {
|
||||
fit: true,
|
||||
values: [0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
|
||||
}
|
||||
}
|
||||
},
|
||||
point: {
|
||||
r: 4
|
||||
},
|
||||
tooltip: {
|
||||
format: {
|
||||
title: function (recall) {
|
||||
const idx = getIndexForValue(data, recall, "recall");
|
||||
if (idx != -1) {
|
||||
return "Threshold: " + data[idx].threshold;
|
||||
}
|
||||
return "";
|
||||
},
|
||||
value: function (precision, ratio, id) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
zoom: {
|
||||
enabled: true
|
||||
}
|
||||
});
|
||||
}
|
||||
// Precision Recall Curve
|
||||
makeChart('#sg-chart', {{ model.pr_curve|safe }});
|
||||
{% if model.multigen_eval %}
|
||||
// Multi Gen Precision Recall Curve
|
||||
makeChart('#mg-chart', {{ model.mg_pr_curve|safe }});
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
// Predict button handler
|
||||
|
||||
@ -393,7 +393,9 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Threshold</td>
|
||||
<td>{{ pathway.setting.model_threshold }}</td>
|
||||
<td>
|
||||
{{ pathway.setting_with_overrides.model_threshold }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -420,11 +422,15 @@
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>Max Nodes</td>
|
||||
<td>{{ pathway.setting.max_nodes }}</td>
|
||||
<td>{{ pathway.setting_with_overrides.max_nodes }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Max Depth</td>
|
||||
<td>{{ pathway.setting.max_depth }}</td>
|
||||
<td>{{ pathway.setting_with_overrides.max_depth }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Expansion Scheme</td>
|
||||
<td>{{ user.default_setting.expansion_scheme }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@ -150,6 +150,10 @@
|
||||
<td>Max Depth</td>
|
||||
<td>{{ user.default_setting.max_depth }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Expansion Scheme</td>
|
||||
<td>{{ user.default_setting.expansion_scheme }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user