diff options
Diffstat (limited to 'src/templates/base/booking')
-rw-r--r-- | src/templates/base/booking/booking_calendar.html | 207 | ||||
-rw-r--r-- | src/templates/base/booking/booking_delete.html | 11 | ||||
-rw-r--r-- | src/templates/base/booking/booking_detail.html | 303 | ||||
-rw-r--r-- | src/templates/base/booking/booking_grid_item.html | 11 | ||||
-rw-r--r-- | src/templates/base/booking/booking_list.html | 44 | ||||
-rw-r--r-- | src/templates/base/booking/booking_table.html | 37 | ||||
-rw-r--r-- | src/templates/base/booking/quick_deploy.html | 140 | ||||
-rw-r--r-- | src/templates/base/booking/stats.html | 71 | ||||
-rw-r--r-- | src/templates/base/booking/steps/booking_meta.html | 38 |
9 files changed, 862 insertions, 0 deletions
diff --git a/src/templates/base/booking/booking_calendar.html b/src/templates/base/booking/booking_calendar.html new file mode 100644 index 0000000..450c97f --- /dev/null +++ b/src/templates/base/booking/booking_calendar.html @@ -0,0 +1,207 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% load bootstrap4 %} + +{% block extrahead %} + {{ block.super }} + + <link href="{% static "bower_components/fullcalendar/dist/fullcalendar.css" %}" + rel='stylesheet'/> + <link href="{% static "bower_components/eonasdan-bootstrap-datetimepicker/build/css/bootstrap-datetimepicker.min.css" %}" + rel='stylesheet'/> +{% endblock extrahead %} + +{% block content %} + <div class="col-lg-8"> + <div class="container-fluid"> + <div class="panel panel-default"> + <div class="panel-heading"> + <i class="fa fa-calendar fa-fw"></i>Calendar + </div> + <div class="panel-body"> + <div id='calendar'> + </div> + </div> + </div> + </div> + </div> + + <div class="col-lg-4"> + <div class="panel panel-default"> + <div class="panel-heading"> + <i class="fa fa-edit fa-fw"></i>Booking + </div> + <div class="panel-body"> + {% if user.is_authenticated %} + <div id="booking_form_div"> + {% bootstrap_form_errors form type='non_fields' %} + <form method="post" action="" class="form" id="bookingform"> + {% csrf_token %} + + <div class='input-group' id='starttimepicker'> + {% bootstrap_field form.start addon_after='<span class="glyphicon glyphicon-calendar"></span>' %} + </div> + <div class='input-group' id='endtimepicker'> + {% bootstrap_field form.end addon_after='<span class="glyphicon glyphicon-calendar"></span>' %} + </div> + {% bootstrap_field form.opsys %} + {% bootstrap_field form.purpose %} + {% bootstrap_field form.installer %} + {% bootstrap_field form.scenario %} + {% buttons %} + <button type="submit" class="btn btn btn-success"> + Book + </button> + <p> + <br>By continuing, you agree to our + <a href="https://wiki.opnfv.org/display/INF/Lab-as-a-Service+at+the+UNH-IOL#Lab-as-a-ServiceattheUNH-IOL-AcceptableUsagePolicy">Acceptable Usage Policy</a> + </p> + {% endbuttons %} + </form> + </div> + <div id="booking_edit_form_div"> + {% bootstrap_form_errors form type='non_fields' %} + <form method="post" action="" class="form" id="bookingeditform"> + {% csrf_token %} + <div id='hide_if_noext'> + <div class='input-group' id='starttimeeditpicker'> + {% bootstrap_field form.start addon_after='<span class="glyphicon glyphicon-calendar"></span>' %} + </div> + <div class='input-group' id='endtimeeditpicker'> + {% bootstrap_field form.end addon_after='<span class="glyphicon glyphicon-calendar"></span>' %} + </div> + </div> + <script type="text/javascript"> + if( {{ booking.ext_count }} <= 0 ){ + var hidediv = document.getElementById('hide_if_noext'); + hidediv.style.display = 'none'; + var par = document.createElement("div"); + var text = document.createTextNode("No Extensions Remaining"); + par.appendChild(text); + par.style.fontWeight = 'bold'; + par.style.paddingBottom = '15pt'; + hidediv.parentNode.insertBefore(par,hidediv.nextSibling); + } + </script> + {% bootstrap_field form.opsys %} + {% bootstrap_field form.purpose %} + {% bootstrap_field form.installer %} + {% bootstrap_field form.scenario %} + {% bootstrap_field form.reset %} + {% buttons %} + <button type="submit" class="btn btn btn-success"> + Confirm Edit + </button> + {% endbuttons %} + </form> + </div> + <script type="text/javascript"> + //Check if current view is an edit or a standard booking view + if(window.location.href.includes('edit')) { + var element = document.getElementById("bookingform"); + element.parentNode.removeChild(element); } + else { + var element = document.getElementById("bookingeditform"); + element.parentNode.removeChild(element); } + </script> + {% else %} + <p>Please + <a href="{% url 'account:login' %}"> + login with Jira</a> + to book this Pod</p> + {% endif %} + </div> + </div> + </div> + + <div id="booking_detail_modal" class="modal fade" role="dialog"> + <div class="modal-dialog"> + + <!-- Modal content--> + <div class="modal-content"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal">×</button> + <h4 class="modal-title">Booking Detail</h4> + </div> + <div class="modal-body" id="booking_detail_content"> + </div> + <div class="modal-footer"> + <button type="button" class="btn btn-primary" data-dismiss="modal">Close + </button> + </div> + </div> + + </div> + </div> +{% endblock content %} + +{% block extrajs %} + <script type="text/javascript"> + var bookings_url = "{% url 'booking:bookings_json' resource_id=resource.id %}"; + var booking_detail_prefix = "{% url 'booking:detail_prefix' %}"; + var booking_delete_prefix = "{% url 'booking:delete_prefix' %}"; + var user_timezone = "{{ request.user.userprofile.timezone }}" + {% autoescape off %} + var sup_installer_dict = {{ installer_filter }} + var sup_scenario_dict = {{ scenario_filter }} + {% endautoescape %} + + </script> + <script type="text/javascript"> + $(document).ready(function () { + $("#id_installer").children().hide(); + $("#id_scenario").children().hide(); + }); + + function installerHider() { + dropFilter("id_installer", sup_installer_dict, "id_opsys"); + scenarioHider(); + } + document.getElementById('id_opsys').addEventListener('change', installerHider); + + function scenarioHider() { + dropFilter("id_scenario", sup_scenario_dict, "id_installer"); + } + document.getElementById('id_installer').addEventListener('change', scenarioHider); + + function dropFilter(target, target_filter, master) { + ob = document.getElementById(target); + + for(var i=0; i<ob.options.length; i++) { + if ( ob.options[i].text == '---------' ) { + ob.selectedIndex = i; + } + } + + targ_id = "#" + target; + $(targ_id).children().hide(); + var drop = document.getElementById(master); + var opts = target_filter[drop.options[drop.selectedIndex].value]; + if (!opts) { + opts = {}; + } + var emptyMap = {} + + var map = Object.create(null); + for (var i = 0; i < opts.length; i++) { + var j = opts[i]; + map[j] = true; + } + + for (var i = 0; i < document.getElementById(target).childNodes.length; i++) { + if (document.getElementById(target).childNodes[i].value in opts && !(document.getElementById(target).childNodes[i].value in emptyMap) ) { + document.getElementById(target).childNodes[i].style.display = "inherit"; + } + } + } + + </script> + <script src={% static "bower_components/moment/moment.js" %}></script> + <script src={% static "bower_components/fullcalendar/dist/fullcalendar.js" %}></script> + <script type="text/javascript" + src={% static "bower_components/eonasdan-bootstrap-datetimepicker/build/js/bootstrap-datetimepicker.min.js" %}></script> + <script src={% static "js/fullcalendar-options.js" %}></script> + <script src={% static "js/datetimepicker-options.js" %}></script> + <script src={% static "js/booking-calendar.js" %}></script> +{% endblock extrajs %} diff --git a/src/templates/base/booking/booking_delete.html b/src/templates/base/booking/booking_delete.html new file mode 100644 index 0000000..b89eb15 --- /dev/null +++ b/src/templates/base/booking/booking_delete.html @@ -0,0 +1,11 @@ +{% load jira_filters %} +{% load bootstrap4 %} + +<p> + Really delete Booking from {{ booking.start}} to {{ booking.end }}? +</p> +<p> + <div id="booking_delete_form_div"> + <a href="{% url 'booking:delete_booking' booking_id=booking.id %}" class="btn btn btn-danger">Delete Booking</a> + </div> +</p>
\ No newline at end of file diff --git a/src/templates/base/booking/booking_detail.html b/src/templates/base/booking/booking_detail.html new file mode 100644 index 0000000..fea2bb6 --- /dev/null +++ b/src/templates/base/booking/booking_detail.html @@ -0,0 +1,303 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load bootstrap4 %} + +{% block extrahead %} + {{block.super}} + <script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?lang=yaml"></script> +{% endblock %} + +{% block content %} +<div class="row"> + <div class="col-12 col-lg-5"> + <div class="card mb-3"> + <div class="card-header d-flex"> + <h4 class="d-inline">Overview</h4> + <button data-toggle="collapse" data-target="#panel_overview" class="btn btn-outline-secondary ml-auto">Expand</button> + </div> + <div class="collapse show" id="panel_overview"> + <table class="table m-0"> + <tr> + <td>Purpose</td> + <td>{{ booking.purpose }}</td> + </tr> + <tr> + <td>Project</td> + <td>{{ booking.project }}</td> + </tr> + <tr> + <td>Start Time</td> + <td>{{ booking.start }}</td> + </tr> + <tr> + <td>End Time</td> + <td>{{ booking.end }}</td> + </tr> + <tr> + <td>Pod Definition</td> + <td>{{ booking.resource.template }}</td> + </tr> + <tr> + <td>Pod Configuration</td> + <td>{{ booking.config_bundle }}</td> + </tr> + <tr> + <td>Lab Deployed At</td> + <td>{{ booking.lab }}</td> + </tr> + </table> + </div> + </div> + <div class="card my-3"> + <div class="card-header d-flex"> + <h4 class="d-inline">Pod</h4> + <button data-toggle="collapse" data-target="#pod_panel" class="btn btn-outline-secondary ml-auto">Expand</button> + </div> + <div class="collapse show" id="pod_panel"> + <div class="card-body"> + {% for host in booking.resource.hosts.all %} + <h4>{{host.template.resource.name}}</h4> + <div class="overflow-auto"> + <table class="table m-0"> + <tr> + <td>Hostname:</td> + <td>{{host.template.resource.name}}</td> + </tr> + <tr> + <td>Machine:</td> + <td>{{host.name}}</td> + </tr> + <tr> + <td>Role:</td> + <td>{{host.config.opnfvRole}}</td> + </tr> + <tr> + <td>Image:</td> + <td id="host_image_{{host.id}}"> + {{host.config.image}} + <button + class="btn btn-primary ml-4" + data-toggle="modal" + data-target="#imageModal" + onclick="set_image_dropdown('{{host.profile.name}}', {{host.id}});" + >Change/Reset</button></td> + </tr> + <tr> + <td>RAM:</td> + <td>{{host.profile.ramprofile.first.amount}}G, + {{host.profile.ramprofile.first.channels}} channels</td> + </tr> + <tr> + <td>CPU:</td> + <td> + <table class="table m-0"> + <tr> + <td>Arch:</td> + <td>{{host.profile.cpuprofile.first.architecture}}</td> + </tr> + <tr> + <td>Cores:</td> + <td>{{host.profile.cpuprofile.first.cores}}</td> + </tr> + <tr> + <td>Sockets:</td> + <td>{{host.profile.cpuprofile.first.cpus}}</td> + </tr> + </table> + </td> + </tr> + <tr> + <td>DISK:</td> + <td> + <table class="table m-0"> + <tr> + <td>Size:</td> + <td>{{host.profile.storageprofile.first.size}} GiB</td> + </tr> + <tr> + <td>Type:</td> + <td>{{host.profile.storageprofile.first.media_type}}</td> + </tr> + <tr> + <td>Mount Point:</td> + <td>{{host.profile.storageprofile.first.name}}</td> + </tr> + </table> + </td> + </tr> + <tr> + <td>Interfaces:</td> + <td> + <table class="table m-0"> + {% for intprof in host.profile.interfaceprofile.all %} + <tr> + <td> + <table class="table table-sm table-borderless m-0"> + <tr> + <td>Name:</td> + <td>{{intprof.name}}</td> + </tr> + <tr> + <td>Speed:</td> + <td>{{intprof.speed}}</td> + </tr> + </table> + </td> + </tr> + {% endfor %} + </table> + </td> + </tr> + </table> + </div> + {% endfor %} + </div> + </div> + </div> + </div> + <div class="col"> + <div class="card mb-3"> + <div class="card-header d-flex"> + <h4 class="d-inline">Deployment Progress</h4> + <p>These are the different tasks that have to be completed before your deployment is ready</p> + <button data-toggle="collapse" data-target="#panel_tasks" class="btn btn-outline-secondary ml-auto">Expand</button> + </div> + <div class="collapse show" id="panel_tasks"> + <table class="table m-0"> + <tr> + <th></th> + <th>Status</th> + <th>Lab Response</th> + <th>Type</th> + </tr> + {% for task in booking.job.get_tasklist %} + <tr> + <td> + {% if task.status < 100 %} + <div class="rounded-circle bg-secondary square-20"></div> + {% elif task.status < 200 %} + <div class="spinner-border text-primary square-20"></div> + {% else %} + <div class="rounded-circle bg-success square-20"></div> + {% endif %} + </td> + <td> + {% if task.status < 100 %} + PENDING + {% elif task.status < 200 %} + IN PROGRESS + {% else %} + DONE + {% endif %} + </td> + <td> + {% if task.message %} + {% if task.type_str == "Access Task" and user_id != task.config.user.id %} + Message from Lab: <pre>--secret--</pre> + {% else %} + Message from Lab: <pre class="text-break pre-wrap">{{ task.message }}</pre> + {% endif %} + {% else %} + No response provided (yet) + {% endif %} + </td> + <td> + {{ task.type_str }} + </td> + </tr> + {% endfor %} + </table> + </div> + </div> + <div class="card my-3"> + <div class="card-header d-flex"> + <h4 class="d-inline">PDF</h4> + <button data-toggle="collapse" data-target="#pdf_panel" class="btn btn-outline-secondary ml-auto">Expand</button> + </div> + <div class="collapse show" id="pdf_panel"> + <div class="card-body"> + <pre class="prettyprint lang-yaml m-0 border-0 text-break pre-wrap"> +{{pdf}} + </pre> + </div> + </div> + </div> + </div> +</div> + + +<div class="modal fade" id="imageModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + <h4 class="modal-title d-inline float-left" id="exampleModalLabel">Host Image</h4> + <button type="button" class="close" data-dismiss="modal" aria-label="Close"> + <span aria-hidden="true">×</span> + </button> + </div> + <div class="modal-body"> + <form id="image_host_form"> + {% csrf_token %} + <select class="form-control" id="image_select" name="image_id"> + </select> + <input id="host_id_input" type="hidden" name="host_id"> + </form> + </div> + <div class="modal-footer d-flex flex-column"> + <div> + <button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button> + <button type="button" class="btn btn-danger" data-toggle="collapse" data-target="#modal_warning" aria-expanded="false">Reset Host</button> + </div> + <div class="border-top collapse mt-3 py-2 text-center w-100" id="modal_warning"> + <h3>Are You Sure?</h3> + <p>This will wipe the disk and reimage the host</p> + <button class="btn btn-outline-secondary" data-dismiss="modal">Nevermind</button> + <button class="btn btn-danger" data-dismiss="modal" onclick="submit_image_form();">I'm Sure</button> + </div> + </div> + </div> + </div> +</div> + +<script> + var image_mapping = {{image_mapping|safe}}; + var current_host_id = 0; + function set_image_dropdown(profile_name, host_id) { + document.getElementById("host_id_input").value = host_id; + current_host_id = host_id; + var dropdown = document.getElementById("image_select"); + var length = dropdown.length; + //clear dropdown + for(i=length-1; i>=0; i--){ + dropdown.options.remove(i); + } + var images = image_mapping[profile_name]; + var image_length = images.length; + for(i=0; i<image_length; i++){ + var opt = document.createElement("OPTION"); + opt.value = images[i].value; + opt.appendChild(document.createTextNode(images[i].name)); + dropdown.options.add(opt); + } + + document.getElementById("modal_warning").classList.add("collapse"); + } + + function submit_image_form() { + var ajaxForm = $("#image_host_form"); + var formData = ajaxForm.serialize(); + req = new XMLHttpRequest(); + req.open("POST", "/booking/modify/{{booking.id}}/image/", true); + req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + req.onerror = function() { alert("problem submitting form"); } + req.onreadystatechange = function() { + if(req.readyState === 4) { + node = document.getElementById("host_image_" + current_host_id); + text = document.createTextNode(req.responseText); + node.replaceChild(text, node.firstChild); + } + } + req.send(formData); + } +</script> +{% endblock content %} diff --git a/src/templates/base/booking/booking_grid_item.html b/src/templates/base/booking/booking_grid_item.html new file mode 100644 index 0000000..3c72fd2 --- /dev/null +++ b/src/templates/base/booking/booking_grid_item.html @@ -0,0 +1,11 @@ +<p>{{ id|default:"id" }}</p> +<p>{{ lab|default:"lab" }}</p> +<p>{{ resource_name|default:"resource name" }}</p> +<p>{{ start|default:"start" }}</p> +<p>{{ end|default:"end" }}<p> +<p>Collaborators:</p> +<ul> + {% for collaborator in collaborators %} + <li>{{ collaborator }}</li> + {% endfor %} +</ul> diff --git a/src/templates/base/booking/booking_list.html b/src/templates/base/booking/booking_list.html new file mode 100644 index 0000000..591ecc9 --- /dev/null +++ b/src/templates/base/booking/booking_list.html @@ -0,0 +1,44 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load bootstrap4 %} + +{% block extrahead %} + {{ block.super }} + <!-- DataTables CSS --> + <link href="{% static "bower_components/datatables.net-bs4/css/dataTables.bootstrap4.min.css" %}" + rel="stylesheet"> + + <!-- DataTables Responsive CSS --> + <link href="{% static "bower_components/datatables.net-responsive-bs4/css/responsive.bootstrap4.min.css" %}" + rel="stylesheet"> +{% endblock extrahead %} + +{% block content %} + <div class="row"> + <div class="col"> + <div class="panel-body"> + <div class="dataTables_wrapper"> + <table class="table table-striped table-bordered table-hover" id="table" + cellspacing="0" + width="100%"> + {% include "booking/booking_table.html" %} + </table> + </div> + </div> + </div> + </div> +{% endblock content %} + +{% block extrajs %} + <!-- DataTables JavaScript --> + <script src={% static "bower_components/datatables.net/js/jquery.dataTables.min.js" %}></script> + <script src={% static "bower_components/datatables.net-bs4/js/dataTables.bootstrap4.min.js" %}></script> + + <script type="text/javascript"> + $(document).ready(function () { + $('#table').DataTable({ + scrollX: true, + }); + }); + </script> +{% endblock extrajs %} diff --git a/src/templates/base/booking/booking_table.html b/src/templates/base/booking/booking_table.html new file mode 100644 index 0000000..32a0146 --- /dev/null +++ b/src/templates/base/booking/booking_table.html @@ -0,0 +1,37 @@ +{% load jira_filters %} + + +<thead> +<tr> + <th>Owner</th> + <th>Purpose</th> + <th>Project</th> + <th>Start</th> + <th>End</th> + <th>Operating System</th> +</tr> +</thead> +<tbody> +{% for booking in bookings %} + <tr> + <td> + {{ booking.owner.username }} + </td> + <td> + {{ booking.purpose }} + </td> + <td> + {{ booking.project }} + </td> + <td> + {{ booking.start }} + </td> + <td> + {{ booking.end }} + </td> + <td> + {{ booking.resource.get_head_node.config.image.os.name }} + </td> + </tr> +{% endfor %} +</tbody> diff --git a/src/templates/base/booking/quick_deploy.html b/src/templates/base/booking/quick_deploy.html new file mode 100644 index 0000000..8570f25 --- /dev/null +++ b/src/templates/base/booking/quick_deploy.html @@ -0,0 +1,140 @@ +{% extends "base.html" %} +{% load staticfiles %} +{% load bootstrap4 %} +{% block content %} + +{% bootstrap_form_errors form type='non_fields' %} +<form id="quick_booking_form" action="/booking/quick/" method="POST" class="form"> + {% csrf_token %} + <div class="row mx-0 px-0"> + <div class="col-12 mx-0 px-0 mt-2"> + <p class="my-0">Please select a host type you wish to book. Only available types are shown.</p> + {% bootstrap_field form.filter_field show_label=False %} + </div> + </div> + <div class="row"> + <div class="col-12 col-lg-3 my-2"> + <div class="col border rounded py-2 h-100"> + {% bootstrap_field form.purpose %} + {% bootstrap_field form.project %} + {% bootstrap_field form.length %} + <span>Days: </span><output id="daysout">0</output> + <script> + document.getElementById("id_length").setAttribute("oninput", "daysout.value=this.value"); + document.getElementById("daysout").value = document.getElementById("id_length").value; + </script> + </div> + </div> + <div class="col-12 col-lg-3 my-2"> + <div class="col border rounded py-2 h-100"> + <label>Collaborators</label> + {{ form.users }} + </div> + </div> + <div class="col-12 col-lg-3 my-2"> + <div class="col border rounded py-2 h-100"> + {% bootstrap_field form.hostname %} + {% bootstrap_field form.image %} + </div> + </div> + <div class="col-12 col-lg-3 my-2"> + <div class="col border rounded py-2 h-100"> + <strong>OPNFV: (Optional)</strong> + {% bootstrap_field form.installer %} + {% bootstrap_field form.scenario %} + </div> + </div> + <div class="col-12 d-flex mt-2 justify-content-end"> + <button id="quick_booking_confirm" onclick="submit_form();" type="button" class="btn btn-success">Confirm</button> + </div> + </div> +</form> + +<script type="text/javascript"> + function submit_form() + { + run_form_callbacks(); + document.getElementById("quick_booking_form").submit(); + } + + function hide_dropdown(drop_id) { + var drop = document.getElementById(drop_id); + //select 'blank' option + for( var i=0; i < drop.length; i++ ) + { + if ( drop.options[i].text == '---------' ) + drop.selectedIndex = i; + } + } + + function get_selected_value(key){ + for( var attr in multi_filter_widget.result[key] ){ + if(!(attr in {}) ) + return attr; + } + } + + var sup_image_dict = {{image_filter | safe}}; + var sup_installer_dict = {{installer_filter | safe}}; + var sup_scenario_dict = {{scenario_filter | safe}}; + + function imageFilter() { + var drop = document.getElementById("id_image"); + var lab_pk = get_selected_value("lab"); + var host_pk = get_selected_value("host"); + + for (const childNode of drop.childNodes) { + var image_object = sup_image_dict[childNode.value]; + if (image_object) //weed out empty option + { + childNode.disabled = !(image_object.host_profile == host_pk && image_object.lab == lab_pk); + } + } + } + + imageFilter(); + $('#id_installer').children().hide(); + $('#id_scenario').children().hide(); + + + Array.from(document.getElementsByClassName("grid-item-select-btn")).forEach(function (element) { + element.addEventListener('click', imageFilter); + }); + + function installerHider() { + dropFilter("id_installer", sup_installer_dict, "id_image"); + scenarioHider(); + } + document.getElementById('id_image').addEventListener('change', installerHider); + + function scenarioHider() { + dropFilter("id_scenario", sup_scenario_dict, "id_installer"); + } + document.getElementById('id_installer').addEventListener('change', scenarioHider); + + function dropFilter(target, target_filter, master) { + var dropdown = document.getElementById(target); + + hide_dropdown(target); + + var drop = document.getElementById(master); + var opts = target_filter[drop.options[drop.selectedIndex].value]; + if (!opts) { + opts = {}; + } + + var map = Object.create(null); + for (var i = 0; i < opts.length; i++) { + var j = opts[i]; + map[j] = true; + } + + for (var i = 0; i < dropdown.childNodes.length; i++) { + if (dropdown.childNodes[i].value in opts && !(dropdown.childNodes[i].value in {})) { + dropdown.childNodes[i].style.display = "inherit"; + dropdown.childNodes[i].disabled = false; + } + } + } +</script> +{% endblock %} diff --git a/src/templates/base/booking/stats.html b/src/templates/base/booking/stats.html new file mode 100644 index 0000000..94239f9 --- /dev/null +++ b/src/templates/base/booking/stats.html @@ -0,0 +1,71 @@ +{% extends "base.html" %} +{% load staticfiles %} + +{% block extrahead %} +{{ block.super }} +<script src="https://cdn.plot.ly/plotly-latest.min.js"></script> +<script> +function drawGraph(data, graph_id, graph_title){ + var container = document.getElementById(graph_id); + var plot_data = { x: data[0], + y: data[1], + fill: "tonexty", + type: "scatter", + mode: "none", + }; + var layout = { + title: graph_title + }; + Plotly.newPlot(container, [plot_data], layout); +} +function getData(){ + var req = new XMLHttpRequest(); + var url = "/booking/stats/json"; + var day_input = document.getElementById("number_days"); + var days = day_input.value; + //var days = document.getElementById("number_days").value; + if(days){ + url = url + "?days=" + days; + } + req.onreadystatechange = function(){ + if( req.readyState == XMLHttpRequest.DONE) { + var data = JSON.parse(req.responseText); + drawGraph(data["booking"], "booking_graph_container", "Active Bookings"); + drawGraph(data["user"], "user_graph_container", "Active Users"); + } + } + req.open("GET", url, true); + req.send(); +} +</script> +{% endblock %} + +{% block content %} +<div class="row"> + <div class="col-auto"> + <p>Number of days to plot: </p> + <div class="form-group d-flex align-content-center"> + <input id="number_days" type="number" class="form-control d-inline-block w-auto" min="1" step="1"/> + <button class="btn btn-primary ml-1" onclick="getData();">Submit</button> + </div> + </div> +</div> +<div class="row"> + <div class="col-12 col-md-10"> + <!-- These graphs do NOT get redrawn when the browser size is changed --> + <div id="all_graph_container border" class="mw-100"> + <div id="booking_graph_wrapper"> + <div id="booking_graph_container"/> + </div> + <div id="user_graph_wrapper"> + <div id="user_graph_container"/> + </div> + </div> + </div> +</div> +<script> +var data = {{data|safe}}; +drawGraph(data["booking"], "booking_graph_container", "Active Bookings"); +drawGraph(data["user"], "user_graph_container", "Active Users"); +</script> +{% endblock content %} diff --git a/src/templates/base/booking/steps/booking_meta.html b/src/templates/base/booking/steps/booking_meta.html new file mode 100644 index 0000000..f12496e --- /dev/null +++ b/src/templates/base/booking/steps/booking_meta.html @@ -0,0 +1,38 @@ +{% extends "workflow/viewport-element.html" %} +{% load staticfiles %} + +{% load bootstrap4 %} + +{% block content %} + +{% bootstrap_form_errors form type='non_fields' %} +<form id="step_form" method="POST" class="form"> +{% csrf_token %} +<div id="form_div" class="container-fluid"> + <div class="row"> + <div class="p-4 col"> + {% bootstrap_field form.purpose %} + {% bootstrap_field form.project %} + {% bootstrap_field form.length %} + <span>Days: </span><output id="daysout">0</output> + <script> + document.getElementById("id_length").oninput = function() { daysout.value=this.value; } + document.getElementById("daysout").value = document.getElementById("id_length").value; + </script> + {% bootstrap_field form.info_file %} + <p>You must provide a url to your project's INFO.yaml file if you are a PTL and you are trying to book a POD with multiple servers in it.</p> + {% bootstrap_field form.deploy_opnfv %} + </div> + <div class="p-4 col"> + <p>You may add collaborators on your booking to share resources with coworkers.</p> + {% bootstrap_field form.users label="Collaborators" %} + </div> + </div> + <div class="panel_wrap"> + {% buttons %} + <button type="submit" class="btn btn-success d-none">Confirm</button> + {% endbuttons %} + </div> +</div> +</form> +{% endblock content %} |