{% extends "base.twig" %}
{% block modal %}
<!-- Add dictionary modal content -->
<div id="addDataModal" class="modal fade" role="dialog" aria-labelledby="addDataModalLabel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="addDataModalLabel">Add Dictionary</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- /.modal-header -->
<div class="modal-body">
<form class="form-horizontal" role="form">
<div class="form-group row has-feedback">
<label class="col-sm-3 control-label" for="username">Username</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="username" id="username" placeholder="Username">
</div>
<!-- col-12 col-sm-9 -->
</div>
<!-- form-group -->
<div class="form-group row has-feedback">
<label class="col-sm-3 control-label" for="firstname">First Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="firstname" id="firstname" placeholder="First Name">
</div>
<!-- col-12 col-sm-9 -->
</div>
<!-- form-group -->
<div class="form-group row has-feedback">
<label class="col-sm-3 control-label" for="lastname">Last Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="lastname" id="lastname" placeholder="Last Name">
</div>
<!-- col-12 col-sm-9 -->
</div>
<!-- form-group -->
<div class="form-group row has-feedback">
<label class="col-sm-3 control-label" for="password">Password</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="password" id="password" placeholder="Password">
</div>
<!-- col-12 col-sm-9 -->
</div>
<!-- form-group -->
<div class="form-group row">
<div class="col-sm-9 offset-sm-3">
<button type="submit" class="btn btn-primary">Add</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">Cancel</button>
</div>
<!-- col-12 col-sm-10 -->
</div>
<!-- form-group -->
</form>
<!-- form-horizontal -->
</div>
<!-- /.modal-body -->
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.addDataModal -->
<!-- Delete modal content -->
<div id="delete-modal" class="modal fade" role="dialog" aria-labelledby="delete-modal-label">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="delete-user-modal-title">Delete</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- /.modal-header -->
<div class="modal-body">
<p>Delete Dictionary or Data? </p>
</div>
<!-- /.modal-body -->
<div class="modal-footer">
<button type="button" class="btn btn-danger" id="chose-delete-dictionary-button">Delete Dictionary</button>
<button type="button" class="btn btn-danger" id="chose-delete-data-button">Delete Data</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.delete-modal -->
<!-- Import progress modal -->
<div id="main-progress-modal" class="modal" data-keyboard="false" data-backdrop="static">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"><h4>Processing....</h4>
<div class="progress">
<div style="width:0%" class="progress-bar progress-bar-striped progress-bar-animated"
role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="2">
<div class="progressText">0% Complete</div>
</div>
</div>
<!-- /.progress -->
</div>
<!-- /.modal-header -->
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.main-progress-modal -->
<!-- Import area names modal content -->
<div id="import-area-names-modal" class="modal fade" role="dialog" aria-labelledby="import-area-names-modal-label">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="import-area-names-modal-label">Import Report Labels</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- /.modal-header -->
<div class="modal-body">
<form class="form-vertical" id="import-user-form" role="form">
<div class="row">
<div class="col-12">
<div id="import-modal-alert" class='alert-placeholder-cspro alert-margin-cspro' ></div>
<!-- /.alert -->
</div>
<!-- /.col-12 -->
</div>
<!-- /.row -->
<div class="form-group row" >
<label for="import-file-name" class="control-label">Import File:</label>
<div class="input-group">
<input type="text" class="form-control" id="import-file-name" readonly>
<label class="input-group-append">
<span class="btn btn-primary">
Browse… <input type="file" id="import-file" style="display: none;" accept=".csv">
</span>
</label>
</div>
<!-- /.input-group -->
</div>
<!-- /.form-group -->
<div class="form-group row">
<div>
<div class="checkbox">
<label>
<input id="import-header-flag" type="checkbox"> CSV file has a header row
</label>
</div>
<!-- /.checkbox -->
</div>
</div>
<!-- /.form-group -->
</form>
<!-- /.form-vertical -->
</div>
<!-- /.modal-body -->
<div class="form-group">
<div class="modal-footer">
<button type="submit" class="btn btn-primary" id="import-button">Import</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="import-help-button">Help</button>
</div>
<!-- /.modal-footer -->
</div>
<!-- /.form-group -->
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.import-area-names-modal -->
<!-- Delete area names modal content -->
<div id="delete-area-names-modal" class="modal fade" role="dialog" aria-labelledby="delete-area-names-modal-label">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="delete-area-names-modal-title">Delete Report Labels</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<!-- /.modal-header -->
<div class="modal-body">
<p>Are you sure you want to delete the labels used in the report?</p>
</div>
<!-- /.modal-body -->
<div class="modal-footer">
<button type="button" class="btn btn-danger" id="delete-area-names-yes-button">Yes</button>
<button type="button" class="btn btn-primary" data-dismiss="modal">No</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!-- /.delete-area-names-modal -->
{% endblock %}
{# modal #}
{% block helps %}
<li class="nav-item mx-1" >
<a href="https://www.csprousers.org/help/CSWeb/accessing_data.html" target="_blank" class="nav-text-cspro"><i class="far fa-question-circle"></i> Help</a>
</li>
{% endblock %}
{# helps #}
{% block content %}
<div class="row">
<div class="col-12">
<div id="alert" class="alert-placeholder-cspro alert-margin-cspro">
</div>
<!-- /.alert -->
</div>
<!-- /.col-12 -->
</div>
<!-- /.row -->
<div class="row">
<div class="col-12">
<div class="card shadow mb-4">
<div class="card-header py-2">
<i class="fas fa-database fa-fw"></i> Data
<div class="btn-toolbar float-right">
<label class="btn btn-primary btn-sm btn-file float-right m-1">
Add Dictionary <input type="file" id="fileInput" accept=".dcf" style="display: none;"/>
</label>
<button type="button" class="btn btn-primary btn-sm float-right m-1" id="import-area-names-button">Import Report Labels</button>
<button type="button" class="btn btn-danger btn-sm float-right m-1" id="delete-area-names-button">Delete Report Labels</button>
</div>
</div>
<!-- /.panel-heading -->
<div class="card-body">
<div class="table-responsive">
<table id="dictionary-data-table" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th class="col-4">Dictionary Label</th>
<th class="col-4">Dictionary Name</th>
<th class="col-2">Cases</th>
<th style="text-align: center" class="col-1">Download</th>
<th style="text-align: center" class="col-1">Delete</th>
</tr>
</thead>
<tbody>
{% for dict in dictionaries %}
<tr data-dictname={{ dict.name}} data-dictlabel="{{ dict.label}}">
<td>{{ dict.label}}</td>
<td>{{ dict.name}}</td>
<td class="caseCount">{{ dict.caseCount}}</td>
<td style="text-align: center"><a href="{{path('downloadDictionary',{'dictname':dict.name}, true)}}"><i class="fas fa-download fa-fw"></i></a></td>
<td style="text-align: center"><a href="javascript:;" class="delete-button-class"><i class="fas fa-trash-alt fa-fw"></i></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- /.table-responsive -->
</div>
<!-- /.panel -->
</div>
<!-- /.col-12 -->
</div>
<!-- /.row -->
{% endblock %}
{# content #}
{% block scripts %}
<!-- Page-Level Scripts -->
<script>
"use strict";
$(document).ready(function () {
$("#data-nav-item").removeClass('nav-item');
$("#data-nav-item").addClass('nav-item active');
if (sessionStorage.getItem("add-dict-success")) {
ui.showAlert(sessionStorage.getItem("add-dict-success"), "alert alert-success");
sessionStorage.clear();
} else if (sessionStorage.getItem("add-dict-failure")) {
ui.showAlert(sessionStorage.getItem("add-dict-failure"), "alert alert-danger");
sessionStorage.clear();
}
$('#dictionary-data-table').DataTable({
columnDefs: [{
orderable: false,
targets: [3,4]
}]
});
$("#fileInput").change(db.uploadDict);
// Delete Dictionary OR Data functionality
(function () {
var tr;
$(".delete-button-class").on("click", function (event) {
tr = $(event.target).closest("tr");
var dictionaryName = $(tr).data("dictname");
var dictionaryLabel = $(tr).data("dictlabel");
var modalText = 'Do you want to delete the "' + dictionaryLabel + ' (' + dictionaryName + ')" dictionary, or the data in it?';
$("#delete-modal").find('.modal-body').text(modalText);
$("#delete-modal").modal("show");
});
$("#chose-delete-data-button").on("click", function (event) {
event.preventDefault();
$("#delete-modal").modal("hide");
// get the user name of the nearest row
var dictionaryName = $(tr).data("dictname");
var dictionaryLabel = $(tr).data("dictlabel");
$.ajax({
type: "DELETE",
url: "{{ path('dashboard') }}" + "/dictionaries/" + dictionaryName + "/data",
dataType: "json",
success: function (response) {
if (response.code === 200) {
var alertMsg = 'Delete data for the dictionary "' + dictionaryLabel + ' (' + dictionaryName + ')" successful';
ui.showAlert(alertMsg, "alert alert-success");
// alw - update UI to show 0 for count cases immediately. future refreshes will use value from dict.caseCount.
$(tr).find("td.caseCount").html('0');
} else {
var alertMsg = 'Failed to delete data for the dictionary "' + dictionaryLabel + ' (' + dictionaryName + ')"';
alertMsg += " " + response.code + " " + response.Message;
ui.showAlert(alertMsg, "alert alert-danger");
}
},
error: function (response) {
if (response.status === 401 || response.status === 405) {
window.location.href = "{{ path('logout') }}";
}
},
failure: function (errMsg) {
alert("error!");
}
});
});
$("#chose-delete-dictionary-button").on("click", function (event) {
event.preventDefault();
$("#delete-modal").modal("hide");
// get the user name of the nearest row
var dictionaryName = $(tr).data("dictname");
var dictionaryLabel = $(tr).data("dictlabel");
$.ajax({
type: "DELETE",
url: "{{ path('dashboard') }}" + "/dictionaries/" + dictionaryName,
dataType: "json",
success: function (response) {
if (response.code === 200) {
var alertMsg = 'Delete dictionary "' + dictionaryLabel + ' (' + dictionaryName + ')" successful';
ui.showAlert(alertMsg, "alert alert-success");
$(tr).remove();
} else {
var alertMsg = 'Failed to delete dictionary "' + dictionaryLabel + ' (' + dictionaryName + ')"';
alertMsg += " " + response.code + " " + response.Message;
ui.showAlert(alertMsg, "alert alert-danger");
}
},
error: function (response) {
if (response.status === 401 || response.status === 405) {
window.location.href = "{{ path('logout') }}";
}
},
failure: function (errMsg) {
alert("error!");
}
});
});
})();
// Import/delete report labels
$("#import-area-names-button").click(function () {
var $input = $("#import-file");
$input.replaceWith($input.val('').clone(true));
$("#import-header-flag").removeAttr('checked'); //reset the checkbox header flag
$("#import-file-name").val('');
$("#import-modal-alert").hide();
$("#import-area-names-modal").modal("show");
});
$("#import-file").change(function () {
// Submit the form here;
var files = $("#import-file").get(0).files;
var numFiles = files ? files.length : 0;
var label = $("#import-file").val().replace(/\\/g, '/').replace(/.*\//, '');
$("#import-file-name").val(label);
//console.log(numFiles);
//console.log(label);
//$("#import-file-name").val(files.name);
});
// Import the selected csv file
$("#import-button").on("click", function (event) {
event.preventDefault();
var $modal = $("#import-area-names-modal");
var files = $("#import-file").get(0).files;
var numFiles = files ? files.length : 0;
if (numFiles === 0) {
ui.showImportAlert('Please select a file to import', "alert alert-danger");
} else {
$modal.modal("hide");
// Load up a progress modal
$('.progress .progressText').text(0 + "% Complete");
$('.progress-bar').css('width', 0 + "%");
$('.progress-bar').css('aria-valuenow', '1');
$("#main-progress-modal").modal('show');
// Process the import and show the spinner
csv.importAreaNames();
}
});
$("#import-help-button").on("click", function (event) {
event.preventDefault();
var msg = '<p>To import report labels a CSV file is needed. The CSV file defines the hierarchical levels of';
msg = msg + ' geography and assigns text names to the numeric codes. Each row is a series of';
msg = msg + ' code and label pairs.</p>';
msg = msg + '<br>CSV Field Rules:';
msg = msg + '<ul><li>Each code must be a numeric</li>'
msg = msg + '<li>Each label will be displayed in place of its code</li></ul>';
msg = msg + '<br>CSV Pair Rules:';
msg = msg + '<ul><li>The first pair in the CSV file must define the first dictionary id</li>';
msg = msg + '<li>Each following pair must correspond to a dictionary id in consecutive order</li></ul>';
msg = msg + '<br>CSV Example Row:';
msg = msg + '<ul><li>1, Artesia, 1, Dongo</li></ul>';
msg = msg + '<br>';
msg = msg + '<p><a href="https://www.csprousers.org/help/CSWeb/sync_report.html" target="_blank">See Viewing Reports for additional information</a></p>';
ui.showImportAlert(msg, "alert alert-info");
});
$("#delete-area-names-button").on("click", function (event) {
$("#delete-area-names-modal").modal("show");
});
$("#delete-area-names-yes-button").on("click", function (event) {
$("#delete-area-names-modal").modal("hide");
db.deleteAreaNames();
});
// Spinner functionality
var $body = $("body");
$(document).ajaxStart(function (event, request, settings) {
$body.addClass("loading");
});
$(document).ajaxComplete(function (event, request, settings) {
$body.removeClass("loading");
});
});
var db =
{
uploadDict: function () {
var fileInput = $("#fileInput");
var file = fileInput.prop('files')[0];
var reader = new FileReader();
reader.onload = function (e) {
var rawBytes = e.target.result;
$("#mainSpinner").show();
$.ajax({
url: "{{ url('upload') }}",
type: "POST",
contentType: "text/plain",
data: rawBytes,
dataType: "json",
processData: false,
success: function (response) {
if (response.code === 200) {
sessionStorage.setItem("add-dict-success", 'Add dictionary successful');
location.reload();
} else {
var jsonResponse = $.parseJSON(response.responseText);
sessionStorage.setItem("add-dict-failure", "Failed to add dictionary. " + jsonResponse.message);
ui.showAlert(sessionStorage.getItem("add-dict-failure"), "alert alert-danger");
}
},
error: function (response) {
try {
var jsonResponse = $.parseJSON(response.responseText);
ui.showAlert("Failed to add dictionary. " + jsonResponse.message, "alert alert-danger");
} catch (err) {
ui.showAlert("Failed to add dictionary. " + response.responseText, "alert alert-danger");
}
},
});
}
reader.readAsArrayBuffer(file);
var fileInput = $("#fileInput");
fileInput.replaceWith(fileInput.val('').clone(true));
},
deleteAreaNames: function () {
// Drop cspro_area_names table
return $.ajax({
type: "DELETE",
url: "{{ url('delete-area-names') }}",
dataType: "json"
}).then(function (response) {
// Done deleteAreaNames
if (response.type === "error") {
// Only invalid responses will have a type
ui.showAlert(response.message, "alert alert-danger");
// Convert success to failure
return $.Deferred().reject(response).promise();
} else {
ui.showAlert(response, "alert alert-success");
}
}, function (response) {
// Fail deleteAreaNames
if (response.status === 405) {
window.location.href = "{{ path('logout') }}";
} else {
ui.showAlert(response.message, "alert alert-danger");
}
});
}
}
var ui =
{
showAlert: function (message, alertStyle) {
$("#alert").replaceWith("<div id='alert' class='" + alertStyle + " alert-margin-cspro'>" + message + "</div>");
$("#alert").show();
},
clearAlert: function () {
$("#alert").replaceWith("<div id='alert' class='alert-placeholder-cspro alert-margin-cspro'></div>");
$("#alert").show();
},
showImportAlert: function (message, alertStyle) {
$("#import-modal-alert").replaceWith("<div id='import-modal-alert' class='" + alertStyle + " alert-margin-cspro'>" + message + "</div>");
$("#import-modal-alert").show();
}
}
var csv =
{
importAreaNames: function () {
var files = $("#import-file").get(0).files;
var file = files[0];
var reader = new FileReader();
var authToken = '{{ accessToken }}';
//console.log("before file read ");
reader.onload = function (e) {
var rawBytes = e.target.result;
// Create an html request to import area names by directly calling the rest api role-area-names url
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function (evt) {
if (xhr.readyState === 4) {
// Done
$("#main-progress-modal").modal('hide');
if (xhr.status === 401) {
window.location.href = "{{ path('logout') }}";
} else if (xhr.status !== 200) {
var responseText = evt.currentTarget.response.toString();
var errResponse = JSON.parse(responseText);
var message = errResponse.message != null ? errResponse.message : responseText;
ui.showAlert(message, "alert alert-danger");
$("#main-progress-modal").modal('hide');
}
}
};
xhr.addEventListener("progress", function (evt) {
//console.log("The response status " + evt.currentTarget.status);
var lines = evt.currentTarget.response.toString().split("\\n");
// alert(lines[0]);
if (evt.currentTarget.status == 200 && lines.length) {
//console.log("number of lines " + lines.length);
var jsonResponse = lines[lines.length - 1];
//console.log("before :" + jsonResponse);
//remove line feeds for json parser
jsonResponse = jsonResponse.replace(/\\n/g, "");
//console.log("after trime: " + test);
var response = JSON.parse(jsonResponse);
if (response.code == 200 || response.code == 206) {
if (response.progress != 100 && response.code == 206) {
//console.log("percent complete" + response.progress);
$('.progress .progressText').text(response.progress + "% Complete");
$('.progress-bar').css('width', response.progress + "%");
$('.progress-bar').css('aria-valuenow', '1');
$("#main-progress-modal").modal('show');
} else {
$('.progress .progressText').text(response.progress + "% Complete");
$('.progress-bar').css('width', response.progress + "%");
$('.progress-bar').css('aria-valuenow', '1');
$("#main-progress-modal").modal('show');
// Give the progress bar at least 3/4 of a second to display
setTimeout(function () {
if (response.count == 1) {
ui.showAlert(response.description + ". " + response.count + " row processed.", "alert alert-success");
} else {
ui.showAlert(response.description + ". " + response.count + " rows processed.", "alert alert-success");
}
$("#main-progress-modal").modal('hide');
}, 750);
}
} else {
// Error
var responseText = evt.currentTarget.response.toString();
var errResponse = JSON.parse(responseText);
var message = errResponse.message != null ? errResponse.message : responseText;
ui.showAlert(message, "alert alert-danger");
$("#main-progress-modal").modal('hide');
}
} else {
/*var message = response.message != null ? response.message : response.description;
ui.showAlert(message, "alert alert-danger");
$("#main-progress-modal").modal('hide');*/
}
}, false);
// Send the request to restAPI
var areaNamesApiUrl = "{{ apiUrl }}" + "import-area-names";
xhr.open('POST', areaNamesApiUrl, true);
xhr.setRequestHeader('Authorization', 'Bearer ' + authToken);
xhr.setRequestHeader('Content-Type', 'text/plain');
xhr.setRequestHeader('Accept', 'application/json');
if ($('#import-header-flag').is(':checked')) {
xhr.setRequestHeader('x-csw-data-header', "1");
}
xhr.send(rawBytes);
} // reader.onload
reader.readAsArrayBuffer(file);
}
}
</script>
{% endblock %}
{# scripts #}