diff --git a/validated/static/validated/daily_validation.js b/validated/static/validated/daily_validation.js index 39cb5706..043b25e1 100755 --- a/validated/static/validated/daily_validation.js +++ b/validated/static/validated/daily_validation.js @@ -1,89 +1,115 @@ +// Define the date format var dateFormat = "yy-mm-dd"; +// Define the initial and final HTML for table rows tr_ini = ''; tr_fin = ''; + +// Initialize an empty object to hold the JSON data var json_data = {}; + +// Initialize arrays to hold the date, value, maximum, and minimum data var data_date = []; var data_value = []; var data_maximum = []; var data_minimum = []; + +// Initialize a counter for the number of data points var num_datos = 0; +// Define an object to hold the subhourly indicators var indicators_subhourly = { - num_time: 0, - num_value: 0, - num_maximum: 0, - num_minimum:0, - num_stddev: 0, + num_time: 0, // Number of time data points + num_value: 0, // Number of value data points + num_maximum: 0, // Number of maximum data points + num_minimum:0, // Number of minimum data points + num_stddev: 0, // Number of standard deviation data points // TODO check if renaming num_data is needed - num_data:0 + num_data:0 // Number of data points } +// Define an object to hold the daily indicators var indicators_daily = { - num_date: 0, - num_percentage: 0, - num_value: 0, - num_maximum: 0, - num_minimum: 0, - num_days: 0 + num_date: 0, // Number of date data points + num_percentage: 0, // Number of percentage data points + num_value: 0, // Number of value data points + num_maximum: 0, // Number of maximum data points + num_minimum: 0, // Number of minimum data points + num_days: 0 // Number of days } +// Initialize a counter for the number of date data points var num_date = 0; + +// Initialize variables to hold the detail and daily table value columns var detail_table_value_columns; var daily_table_value_columns; +// When the document is ready $(document).ready(function() { + // When the "Submit" button is clicked, call the daily_query_submit function $("#btn_submit").click(daily_query_submit); + // When the variable ID changes $("#id_variable").change(function () { + // Get the new variable ID var variable_id = $(this).val(); + // Construct the URL to fetch the variable data var url = '/variable/variable/' + variable_id.toString() + '/?format=json'; const url_total = window.location.origin + url; + // Fetch the variable data fetch(url_total) .then(response => response.json()) .then(data => { + // Update the minimum and maximum values in the user interface $("#id_minimum").val(data.minimum); $("#id_maximum").val(data.maximum); }) .catch(error => { + // Log and alert any errors console.error(error); alert(error); }); }); + // When any of the detail filters change, call the filter_detail_table function $("#chk_detail_time").change(filter_detail_table); $("#chk_detail_value").change(filter_detail_table); $("#chk_detail_stddev").change(filter_detail_table); $("#chk_detail_selected").change(filter_detail_table); $("#chk_detail_value_difference").change(filter_detail_table); + // Get references to various elements in the user interface var $table = $('#table_daily'); - var $btn_daily_send = $('#btn_daily_send'); - var $btn_detail_select = $('#btn_detail_select'); var $btn_detail_unselect = $('#btn_detail_unselect'); var $btn_detail_new = $("#btn_detail_new"); var $btn_detail_save = $('#btn_detail_save'); - var $btn_detail_modify_row = $('#btn_detail_modify_row'); var $btn_detail_new_save = $('#btn_detail_new_save'); - + // When the "Send" button is clicked, call the save_daily function $btn_daily_send.click(save_daily); + // When the "Select" button is clicked, call the check function $btn_detail_select.click(check); + // When the "Unselect" button is clicked, call the uncheck function $btn_detail_unselect.click(uncheck); + // When the "New" button is clicked, call the open_form_new function $btn_detail_new.click(open_form_new); + // When the "Save" button is clicked, call the save_detail function $btn_detail_save.click(save_detail); + // When the "Modify Row" button is clicked, call the detail_modify_row function $btn_detail_modify_row.click(detail_modify_row); + // When the "New Save" button is clicked, call the detail_new_save function $btn_detail_new_save.click(detail_new_save); - + // Set up the datepickers var dateFormat = "yy-mm-dd"; $( "#id_start_date" ).datepicker({ changeMonth: true, @@ -104,6 +130,7 @@ $(document).ready(function() { $( "#id_start_time" ).datepicker( "option", "maxDate", getDate( this ) ); }); + // Function to parse a date from a string function getDate( element ) { var date; try { @@ -116,76 +143,114 @@ $(document).ready(function() { }); +/** + * This function generates traces for a dispersion chart from the provided data. + * @param {Object} data - The data to generate the traces from. + * @param {string} source_type - The source type of the data. + * @param {string} color - The color to use for the traces. + * @returns {Array} An array of trace objects to be used in a dispersion chart. + */ function generate_traces_dispersion(data, source_type, color){ - var result = []; - var columns = Object.keys(data); - columns = columns.filter((e) => e !== "time"); - for (const c of columns) { - result.push( - { - x: data.time, - y: data[c], - mode: 'markers', - name: c, - showlegend: false, - marker: { - color: color, - size: 2 - }, - type: 'scattergl', - legendgroup: source_type + // Initialize an empty array to hold the result + var result = []; + + // Get the column names from the data + var columns = Object.keys(data); + + // Filter out the "time" column + columns = columns.filter((e) => e !== "time"); + + // For each column in the data + for (const c of columns) { + // Push a new trace object to the result array + result.push( + { + x: data.time, // The x-axis data is the "time" column + y: data[c], // The y-axis data is the current column + mode: 'markers', // The mode of the trace is 'markers' + name: c, // The name of the trace is the column name + showlegend: false, // Do not show the legend for this trace + marker: { + color: color, // The color of the trace is the provided color + size: 2 // The size of the trace is 2 + }, + type: 'scattergl', // The type of the trace is 'scattergl' + legendgroup: source_type // The legend group of the trace is the source type + } + ); } - ); - } + // Add a legend trace to the result array + result.push( + { + x: [null], // The x-axis data is null + y: [null], // The y-axis data is null + mode: 'markers', // The mode of the trace is 'markers' + name: source_type, // The name of the trace is the source type + showlegend: true, // Show the legend for this trace + marker: { + color: color, // The color of the trace is the provided color + }, + type: 'scattergl', // The type of the trace is 'scattergl' + legendgroup: source_type // The legend group of the trace is the source type + } + ); - // Legend - result.push( - { - x: [null], - y: [null], - mode: 'markers', - name: source_type, - showlegend: true, - marker: { - color: color, - }, - type: 'scattergl', - legendgroup: source_type - } - ); - return result; + // Return the result array + return result; } +/** + * This function generates traces for a bar chart from the provided data. + * @param {Object} data - The data to generate the traces from. + * @param {string} source_type - The source type of the data. + * @param {string} color - The color to use for the traces. + * @returns {Array} An array of trace objects to be used in a bar chart. + */ function generate_traces_bars(data, source_type, color){ - var result = []; - var columns = Object.keys(data); - columns = columns.filter((e) => e !== "time"); - - for (const c of columns) { - result.push( - { - x: data.time, - y: data[c], - mode: 'lines', - name: c + ' - ' + source_type, - marker: { - color: color, - size: 2 - }, - showlegend: true, - type: 'scattergl', + // Initialize an empty array to hold the result + var result = []; + + // Get the column names from the data + var columns = Object.keys(data); + + // Filter out the "time" column + columns = columns.filter((e) => e !== "time"); + + // For each column in the data + for (const c of columns) { + // Push a new trace object to the result array + result.push( + { + x: data.time, // The x-axis data is the "time" column + y: data[c], // The y-axis data is the current column + mode: 'lines', // The mode of the trace is 'lines' + name: c + ' - ' + source_type, // The name of the trace is the column name and the source type + marker: { + color: color, // The color of the trace is the provided color + size: 2 // The size of the trace is 2 + }, + showlegend: true, // Show the legend for this trace + type: 'scattergl', // The type of the trace is 'scattergl' + } + ); } - ); - } - return result; + // Return the result array + return result; } +/** + * This function generates a bar plot using the Plotly library. + * @param {Object} series - The data for the traces. Each property should be an object with properties "measurement", "validated", and "selected". + * @param {string} append_to - The id of the HTML element where the plot should be appended. + * @param {Object} variable - An object with property "var_nombre" which is the title of the plot. + */ function bar_plot(series, append_to, variable){ var data_array = []; + // Generate traces for "measurement", "validated", and "selected" data and push them to data_array let measurement = generate_traces_bars(series.measurement, "Measurement", 'rgb(0, 0, 255)'); data_array.push(...measurement); let validated = generate_traces_bars(series.validated, "Validated", 'rgb(0, 255, 0)'); @@ -193,20 +258,31 @@ function bar_plot(series, append_to, variable){ let selected = generate_traces_bars(series.selected, "Selected", 'rgb(0, 0, 0)'); data_array.push(...selected); + // Define the layout for the plot var layout = { - title: variable.var_nombre, - showlegend: true, + title: variable.var_nombre, // The title of the plot + showlegend: true, // Show the legend }; + // Set the height and width of the div where the plot will be appended const miDiv = document.querySelector("#" + append_to); miDiv.style.height = "450px"; miDiv.style.width = "850px"; + + // Generate the plot and append it to the div Plotly.newPlot(append_to, data_array, layout, {renderer: 'webgl'}); } +/** + * This function generates a dispersion plot using the Plotly library. + * @param {Object} series - The data for the traces. Each property should be an object with properties "measurement", "validated", and "selected". + * @param {string} append_to - The id of the HTML element where the plot should be appended. + * @param {Object} variable - An object with property "var_nombre" which is the title of the plot. + */ function dispersion_plot(series, append_to, variable){ var data_array = []; + // Generate traces for "measurement", "validated", and "selected" data and push them to data_array let measurement = generate_traces_dispersion(series.measurement, "Measurement", 'rgb(0, 0, 255)'); data_array.push(...measurement); let validated = generate_traces_dispersion(series.validated, "Validated", 'rgb(0, 255, 0)'); @@ -214,33 +290,46 @@ function dispersion_plot(series, append_to, variable){ let selected = generate_traces_dispersion(series.selected, "Selected", 'rgb(0, 0, 0)'); data_array.push(...selected); + // Define the layout for the plot var layout = { - title: variable.var_nombre, - showlegend: true, + title: variable.var_nombre, // The title of the plot + showlegend: true, // Show the legend }; + // Set the height and width of the div where the plot will be appended const miDiv = document.querySelector("#" + append_to); miDiv.style.height = "450px"; miDiv.style.width = "850px"; + + // Generate the plot and append it to the div Plotly.newPlot(append_to, data_array, layout, {renderer: 'webgl'}); } +/** + * This function saves daily data by sending an AJAX POST request to the '/validated/daily_save/' endpoint. + * @param {Object} event - The event object that triggered the function. + */ function save_daily(event){ + // Select the daily and detail tables var $table_daily = $('#table_daily'); var $table_detail = $('#table_detail'); - token = $("input[name='csrfmiddlewaretoken']").val(); - station_id = $("#id_station").val(); - variable_id = $("#id_variable").val(); - maximum = $("#id_maximum").val(); - minimum = $("#id_minimum").val(); - start_date = $("input[name='start_date']").val(); - end_date = $("input[name='end_date']").val(); - changes = JSON.stringify($table_daily.bootstrapTable('getData',{unfiltered:true, includeHiddenRows: true})); + // Gather various pieces of data from the page + var token = $("input[name='csrfmiddlewaretoken']").val(); + var station_id = $("#id_station").val(); + var variable_id = $("#id_variable").val(); + var maximum = $("#id_maximum").val(); + var minimum = $("#id_minimum").val(); + var start_date = $("input[name='start_date']").val(); + var end_date = $("input[name='end_date']").val(); + + // Get the current data in the daily table + var changes = JSON.stringify($table_daily.bootstrapTable('getData',{unfiltered:true, includeHiddenRows: true})); + // Send an AJAX POST request to the '/validated/daily_save/' endpoint with the gathered data $.ajax({ url: '/validated/daily_save/', data: { @@ -255,11 +344,14 @@ function save_daily(event){ }, type:'POST', beforeSend: function () { + // Show a loading state in the daily and detail tables $table_daily.bootstrapTable('showLoading'); $table_detail.bootstrapTable('showLoading'); }, success: function (data) { + // If the request is successful and the server responds with `result: true` if (data.result == true){ + // Show a success message, hide the information div, clean the filters, remove all rows from the daily and detail tables $("#div_body_message").html('Data saved correctly to Validated!') $("#div_validation_message").modal("show"); $("#div_information").hide(); @@ -269,29 +361,41 @@ function save_daily(event){ $table_daily.bootstrapTable('removeAll'); } else{ + // If the server responds with `result: false`, show an error message $("#div_body_message").html('There was a problem with the validation please contact the administrator') $("#div_validation_message").modal("show"); } + // Hide the loading state in the daily and detail tables $table_daily.bootstrapTable('hideLoading'); $table_detail.bootstrapTable('hideLoading'); }, error: function () { + // If the request fails, show an error message and hide the loading state in the daily table $("#div_body_message").html('There was a problem with the validation please contact the administrator') $("#div_validation_message").modal("show"); $table_daily.bootstrapTable('hideLoading'); } }); - } +/** + * This function saves detail data by sending an AJAX POST request to the '/validated/detail_save/' endpoint. + * @param {Object} event - The event object that triggered the function. + */ function save_detail(event){ + // Select the detail table var $table = $('#table_detail'); - token = $("input[name='csrfmiddlewaretoken']").val(); - station_id = $("#id_station").val(); - variable_id = $("#id_variable").val(); - data = JSON.stringify($table.bootstrapTable('getData',{unfiltered:true})); + // Gather various pieces of data from the page + var token = $("input[name='csrfmiddlewaretoken']").val(); + var station_id = $("#id_station").val(); + var variable_id = $("#id_variable").val(); + + // Get the current data in the detail table + var data = JSON.stringify($table.bootstrapTable('getData',{unfiltered:true})); + + // Send an AJAX POST request to the '/validated/detail_save/' endpoint with the gathered data $.ajax({ url: '/validated/detail_save/', data: { @@ -302,12 +406,13 @@ function save_detail(event){ }, type:'POST', beforeSend: function () { + // Show a loading state in the detail table $table.bootstrapTable('showLoading'); }, success: function (response) { - document.getElementById("tab3-tab").style.display = "none"; - console.log(typeof response.resultado) + // If the request is successful and the server responds with `result: true` if (response.result == true){ + // Show a success message, hide the detail tab, clean the filters, destroy the detail table, and refresh the daily data $("#div_body_message").html('Data saved successfully') $("#div_validation_message").modal("show"); $table.bootstrapTable('destroy'); @@ -318,39 +423,52 @@ function save_detail(event){ daily_query_submit(); } else{ + // If the server responds with `result: false`, show an error message and hide the loading state in the detail table $("#div_body_message").html('There was a problem with the validation please contact the administrator') $("#div_validation_message").modal("show"); $table.bootstrapTable('hideLoading'); } }, error: function () { + // If the request fails, show an error message and hide the loading state in the detail table $("#div_body_message").html('There was a problem with the validation please contact the administrator') $("#div_validation_message").modal("show"); $table.bootstrapTable('hideLoading'); } }); - - } +/** + * This function submits a daily query by sending an AJAX POST request to the endpoint specified in the form's action attribute. + * It then processes the response and updates the page accordingly. + */ function daily_query_submit(){ + // Select the daily table and get the variable id var $table_daily = $('#table_daily'); var var_id = $("#id_variable").val(); + + // Initialize error flag and message var flag_error = false; var message = ''; + + // Clean filters and get start and end dates $("#orig_variable_id").val(var_id); - $("#div_information").html('') + $("#div_information").html(''); clean_filters('daily'); clean_filters('detail'); start_date = document.querySelector('input[name="start_date"]').value; end_date = document.querySelector('input[name="end_date"]').value; + + // Check if dates are provided if( start_date == '' || end_date == '') { + // If not, show a message and return $("#div_message_dates").show("slow"); $("#div_c").html(""); } else { + // If dates are provided, hide the message and send the AJAX request $("#div_message_dates").hide(); $("#div_c").html(""); $.ajax({ @@ -358,16 +476,20 @@ function daily_query_submit(){ data: $("#form_validation").serialize(), type:'POST', beforeSend: function () { + // Show a loading state in the daily table $table_daily.bootstrapTable('showLoading'); }, success: function (data) { + // Process the response $("#btn_submit").attr("disabled", false); for (var key in data){ + // If there's an error, set the error flag and message if (key == 'error'){ flag_error = true; message = data.error; } } + // If there's an error, show the error message and return if (flag_error == true){ $table_daily.bootstrapTable('hideLoading'); $("#div_body_message").html(message) @@ -375,22 +497,21 @@ function daily_query_submit(){ return; } - + // If there's no data, show a message and return if (data.data.length < 1){ $("#div_information").show("slow"); $("#div_information").html('

No hay datos

'); return; } + // Update the page with the response data $("#div_c").html(data.curva); - if (data.variable.is_cumulative){ bar_plot(data.series, "div_information", data.variable); }else{ dispersion_plot(data.series, "div_information", data.variable); } -// enable_new(); var_id = data.variable.id; variable = data.variable; station = data.station; @@ -412,28 +533,38 @@ function daily_query_submit(){ $("#table_detail").bootstrapTable('removeAll'); }, error: function () { + // If the request fails, show an error message and hide the loading state in the daily table $table_daily.bootstrapTable('hideLoading'); $("#div_body_message").html('Ocurrio un problema con la validaciĆ³n por favor contacte con el administrador') $("#div_validation_message").modal("show"); - //mostrar_mensaje() } }); - } + // Click the first tab var tab1 = document.getElementById("tab1-tab"); tab1.click(); } +/** + * This function is used to switch between different tabs in a tabbed interface. + * @param {Object} evt - The event object that triggered the function. + * @param {string} tabName - The id of the tab to be displayed. + */ function getTab(evt, tabName) { var i, tabpane; + + // Get all the tab panes tabpane = document.getElementsByClassName("tab-pane"); + + // Loop through all the tab panes and hide them for (i = 0; i < tabpane.length; i++) { tabpane[i].style.display = "none"; tabpane[i].classList.remove("show"); } + // Get the tab pane to be displayed and show it var e = document.getElementById(tabName); e.classList.add("show"); e.style.display = ""; @@ -441,72 +572,121 @@ function getTab(evt, tabName) { - - - - - +/** + * This function checks if a given date exists in a data array. + * @param {string} fecha - The date to be checked. + * @param {Array} datos - The data array where the date will be checked. + * @returns {Array} - An array of data that matches the given date. + */ function get_existe_en_tabla(fecha, datos){ - debugger; + // The 'debugger' statement has been commented out as it's used for debugging purposes and should not be in production code + //debugger; + + // Initialize a flag to check if the date exists in the data var existe = false; + + // Filter the data array to get data that matches the given date return datos.filter( function(datos){ + // If the date of the current data matches the given date, set the 'existe' flag to true + // This line has been commented out as it's not being used //if(datos.fecha == fecha) //existe = true + + // Return true if the date of the current data matches the given date, false otherwise return datos.fecha == fecha } ) - - } +/** + * This function modifies a row in the detail table. + * @param {Object} event - The event object that triggered the function. + */ function detail_modify_row(event){ + // Enable the detail time input $('input[name="detail_time"]').attr('disabled',false); + + // Get the variable id from the form var variable_id = parseInt($("#id_variable").val()); + + // Select the modify form and the modal var $form = $("#form_modify"); var inputs =$form.serializeArray(); var $modal = $("#modal_modify"); + + // Initialize an empty data object and select the detail table var data = {}; var table = $table = $("#table_detail"); + // Loop through the form inputs $.each(inputs, function(i, field){ + // If the input name includes 'detail_', process it if (field.name.includes('detail_')) { + // Get the field name by splitting the input name on '_' var _field = field.name.split("_")[1]; + + // If the field is included in the detail table value columns or is 'id' or 'time', add it to the data object if(detail_table_value_columns.includes(_field) || ['id', 'time'].includes(_field)){ data[_field] = field.value; } } }); + // Get the id from the data object and remove it from the object id = data['id']; delete data['id']; + + // Set 'is_selected' to true in the data object data['is_selected'] = true; + // Loop through the detail table value columns and add error values to the data object for (const c of detail_table_value_columns) { data[ c + '_error'] = get_value_error(data[c]) } + + // Set 'stddev_error' to false in the data object data['stddev_error'] = false; + + // Update the row in the detail table with the data object $table.bootstrapTable('updateByUniqueId',{ id: id, row: data }); + + // Hide the modal $modal.modal('hide'); } +/** + * This function saves a new row in the detail table. + * @param {Object} event - The event object that triggered the function. + */ function detail_new_save(event){ + // Get the variable id from the form var variable_id = parseInt($("#id_variable").val()); + + // Select the new form and the modal var $form = $("#form_new"); var inputs =$form.serializeArray(); var $modal = $("#modal_new"); + + // Initialize an empty data object and select the detail table var data = {}; var table = $table = $("#table_detail"); + // Loop through the form inputs $.each(inputs, function(i, field){ + // If the input name includes 'new_', process it if (field.name.includes('new_')) { + // Get the field name by splitting the input name on '_' var _field = field.name.split("_")[1]; + + // If the field is included in the detail table value columns or is 'date' or 'hour', add it to the data object + // and clear the input field if(detail_table_value_columns.includes(_field) || ['date', 'hour'].includes(_field)){ data[_field] = field.value; $('input[name="'+field.name+'"]').val(""); @@ -514,44 +694,69 @@ function detail_new_save(event){ } }); + // Combine 'date' and 'hour' to create 'time' and add it to the data object + // Then remove 'date' and 'hour' from the data object data['time'] = data['date'] + ' ' + data['hour']; delete data['date']; delete data['hour']; + + // Set 'is_selected' to true in the data object data['is_selected'] = true; + + // Get the last row in the detail table and get its id + // Then increment the id and add it to the data object var last_row = $table.bootstrapTable('getData').slice(-1); var last_id = parseInt(last_row[0]['id']); data['id'] = last_id + 1; + // Append the data object as a new row in the detail table and scroll to the bottom of the table $table.bootstrapTable('append', data); $table.bootstrapTable('scrollTo', 'bottom'); - - + // Hide the modal $modal.modal('hide'); } +/** + * This function checks rows in the detail table based on the ids provided in the detail selection input. + * @param {Object} event - The event object that triggered the function. + */ function check(event){ + // Get the name of the current target of the event var name = event.currentTarget.name; + + // Initialize the detail selection text and the table var tx_selection = ''; var $table = ''; + // If the name is 'detail', select the detail table and get the detail selection text if (name === 'detail'){ $table = $("#table_detail"); tx_selection = $("#txt_detail_selection").val().toString(); } else{ + // If the name is not 'detail', do nothing } + + // Show a loading state in the table $table.bootstrapTable('showLoading'); + + // Split the detail selection text on ',' and '-' var arr_id = tx_selection.split(','); var arr_range = tx_selection.split('-'); + + // Initialize an empty ids array var ids = [] + // If there are multiple ids, map them to integers and add them to the ids array if (arr_id.length>1){ ids = arr_id.map(function(id){ return parseInt(id) }); } + + // If there is a range of ids, generate all the ids in the range and add them to the ids array if (arr_range.length>0){ var start = parseInt(arr_range[0]); var end = parseInt(arr_range[1]); @@ -561,33 +766,54 @@ function check(event){ } } + // Check the rows in the table that match the ids in the ids array $table.bootstrapTable('checkBy', {field: 'id', values: ids}) + + // Hide the loading state in the table and clear the detail selection input $table.bootstrapTable('hideLoading'); $("#txt_detail_selection").val(""); } +/** + * This function unchecks rows in the detail table based on the ids provided in the detail selection input. + * @param {Object} event - The event object that triggered the function. + */ function uncheck(event){ + // Get the name of the current target of the event var name = event.currentTarget.name; + + // Initialize the detail selection text and the table var tx_selection = ''; var $table = ''; + // If the name is 'detail', select the detail table and get the detail selection text if (name === 'detail'){ $table = $("#table_detail"); tx_selection = $("#txt_detail_selection").val().toString(); } else{ + // If the name is not 'detail', do nothing } + + // Show a loading state in the table $table.bootstrapTable('showLoading'); + + // Split the detail selection text on ',' and '-' var arr_id = tx_selection.split(','); var arr_range = tx_selection.split('-'); + + // Initialize an empty ids array var ids = [] + // If there are multiple ids, map them to integers and add them to the ids array if (arr_id.length>1){ ids = arr_id.map(function(id){ return parseInt(id) }); } + + // If there is a range of ids, generate all the ids in the range and add them to the ids array if (arr_range.length>0){ var start = parseInt(arr_range[0]); var end = parseInt(arr_range[1]); @@ -597,21 +823,33 @@ function uncheck(event){ } } + // Uncheck the rows in the table that match the ids in the ids array $table.bootstrapTable('uncheckBy', {field: 'id', values: ids}) + + // Hide the loading state in the table and clear the detail selection input $table.bootstrapTable('hideLoading'); $("#txt_detail_selection").val(""); } +/** + * This function fetches and displays detail data for a specific row in the detail table. + * @param {Object} e - The event object that triggered the function. + * @param {Object} value - The value of the clicked cell. + * @param {Object} row - The data of the clicked row. + */ function detail_details(e, value, row){ + // Select the detail table and get the station and variable ids var $table = $('#table_detail'); var station_id = $("#id_station").val(); var variable_id = $("#id_variable").val(); + // Initialize the daily id and date var id_daily = 0; var date = ''; + // If a row was clicked, get the id and date from the row + // Otherwise, get the original daily id and date var state = row || false - if (state == false ){ id_daily = $("#orig_id_daily").val(); date = $("#orig_date_daily").val(); @@ -622,69 +860,100 @@ function detail_details(e, value, row){ $("#orig_id_daily").val(id_daily); $("#orig_date_daily").val(date); } + + // Update the original detail date date = row.date; $("#orig_detail_date").val(date); - + // Get the maximum and minimum variables var var_maximum = $("#id_maximum").val(); var var_minimum = $("#id_minimum").val(); + // Construct the url for the detail list url = '/validated/detail_list/' + station_id + '/' + variable_id + '/' + date + '/' + var_minimum + '/' + var_maximum; + // Send a GET request to the detail list url $.ajax({ url: url, type:'GET', beforeSend: function () { + // Show a loading state in the table $table.bootstrapTable('showLoading'); }, success: function (data) { + // On success, display the third tab and click it document.getElementById("tab3-tab").style.display = "block"; document.getElementById("tab3-tab").click(); + + // Update the detail table value columns and the json data detail_table_value_columns = data.value_columns; json_data = data.series; + + // Destroy the current table $table.bootstrapTable('destroy'); + + // Update the indicators and their spans for (const index in data.indicators){ indicators_subhourly[index] = data.indicators[index]; - // TODO Verificar $("#span_"+index+"_detail").text(indicators_subhourly[index]); } - + // Replace 'T' with ' ' in the time of each element in the json data for (const element of json_data) { element["time"] = (element['time']).replace('T',' '); } + // Get the columns for the detail table var columns = get_columns_detail(variable_id, data.value_columns); + + // Initialize the table with the columns and data $table.bootstrapTable({ columns:columns, data: json_data, rowStyle: style_row, height: 370, - }); + }); + + // Hide the loading state in the table $table.bootstrapTable('hideLoading'); }, error: function () { + // On error, display a validation message $("#div_body_message").html('An issue occurred with the validation. Please contact the administrator.') $("#div_validation_message").modal("show"); $table.bootstrapTable('hideLoading'); } }); - }; +/** + * This function opens a form for adding a new row to the detail table. + * @param {Object} event - The event object that triggered the function. + */ function open_form_new(event){ + // Get the original detail date and variable id var date = $("#orig_detail_date").val(); var variable_id = parseInt($("#id_variable").val()); + + // Select the form modal and the form var $form_modal = $('#modal_new'); var $form = "#form_new"; + + // Get the form inputs var inputs = $("#form_new").serializeArray(); + // Loop through the form inputs $.each(inputs, function(i, field){ + // If the input name includes 'new_', process it if (field.name.includes('new_')) { + // Get the field name by splitting the input name on '_' var _field = field.name.split("_")[1]; + + // If the field is included in the detail table value columns or is 'date' or 'hour', show the input field + // Otherwise, hide the input field if(detail_table_value_columns.includes(_field) || ['date', 'hour'].includes(_field)){ $('input[name="'+field.name+'"]').parent().show(); }else{ @@ -693,18 +962,34 @@ function open_form_new(event){ } }); + // Set the value of the 'new_date' input field to the original detail date and show the form modal $($form+',input[name="new_date"]').val(date); $form_modal.modal("show"); } +/** + * This function opens a form for updating a specific row in the detail table. + * @param {Object} e - The event object that triggered the function. + * @param {Object} value - The value of the clicked cell. + * @param {Object} row - The data of the clicked row. + * @param {number} index - The index of the clicked row. + */ function open_form_update(e, value, row, index){ + // Select the form modal and get the form inputs var $form_modal = $('#modal_modify'); var inputs = $("#form_modify").serializeArray(); + + // Loop through the form inputs $.each(inputs, function(i, field){ + // If the input name includes 'detail_', process it if (field.name.includes('detail_')) { + // Get the field name by splitting the input name on '_' var _field = field.name.split("_")[1]; + + // If the field is included in the detail table value columns or is 'id' or 'time', show the input field and set its value to the corresponding row value + // Otherwise, hide the input field if(detail_table_value_columns.includes(_field) || ['id', 'time'].includes(_field)){ $('input[name="'+field.name+'"]').parent().show(); $('input[name="'+field.name+'"]').val(row[_field]); @@ -713,21 +998,31 @@ function open_form_update(e, value, row, index){ } } }); + + // Disable the 'detail_time' input field and show the form modal $('input[name="detail_time"]').attr('disabled',true); $form_modal.modal("show"); } - +/** + * This function generates the column configuration for the daily table. + * @param {number} var_id - The variable id. + * @param {Array} value_columns - The value columns to include in the table. + * @returns {Array} The column configuration for the table. + */ function get_columns_daily(var_id, value_columns){ + // Initialize an empty array for the columns var columns = []; + // Define the state column and add it to the columns array var state = { field:'state', checkbox:true }; columns.push(state); + // Define the id column and add it to the columns array var id = { field:'id', title:'Id', @@ -735,26 +1030,26 @@ function get_columns_daily(var_id, value_columns){ }; columns.push(id); + // Define the date column and add it to the columns array var date = { field:'date', title: 'Date', cellStyle: style_date, formatter: format_value, footerFormatter: footer_date, - //filterControl: 'datepicker' }; columns.push(date); - + // Define the percentage column and add it to the columns array var percentage = { field:'percentage', title:'Percnt.', cellStyle: style_percentage, footerFormatter: footer_average, - //filterControl: 'input' }; columns.push(percentage); + // If the value columns include 'sum', define the sum column and add it to the columns array if (value_columns.includes("sum")){ var sum = { field:'sum', @@ -766,6 +1061,7 @@ function get_columns_daily(var_id, value_columns){ columns.push(sum); } + // If the value columns include 'average', define the average column and add it to the columns array if (value_columns.includes("average")){ var average = { field:'average', @@ -777,6 +1073,7 @@ function get_columns_daily(var_id, value_columns){ columns.push(average); } + // If the value columns include 'maximum', define the maximum column and add it to the columns array if (value_columns.includes("maximum")){ var maximum = { field:'maximum', @@ -788,6 +1085,7 @@ function get_columns_daily(var_id, value_columns){ columns.push(maximum); } + // If the value columns include 'minimum', define the minimum column and add it to the columns array if (value_columns.includes("minimum")){ var minimum= { field:'minimum', @@ -799,6 +1097,7 @@ function get_columns_daily(var_id, value_columns){ columns.push(minimum); } + // Define the value difference column and add it to the columns array var value_difference = { field:'value_difference_error_count', title:'Diff. Err', @@ -808,6 +1107,7 @@ function get_columns_daily(var_id, value_columns){ }; columns.push(value_difference); + // Define the action column and add it to the columns array var action = { field: 'action', title: 'Action', @@ -818,20 +1118,29 @@ function get_columns_daily(var_id, value_columns){ }; columns.push(action); + // Return the columns array return columns } +/** + * This function generates the column configuration for the detail table. + * @param {number} var_id - The variable id. + * @param {Array} value_columns - The value columns to include in the table. + * @returns {Array} The column configuration for the table. + */ function get_columns_detail(var_id, value_columns){ + // Initialize an empty array for the columns var columns = []; - var span = 'num'; + // Define the is_selected column and add it to the columns array var is_selected = { field:'is_selected', checkbox:true, }; columns.push(is_selected); + // Define the id column and add it to the columns array var id = { field:'id', title:'Id', @@ -840,6 +1149,7 @@ function get_columns_detail(var_id, value_columns){ }; columns.push(id); + // Define the time column and add it to the columns array var time = { field:'time', title:'Time', @@ -848,26 +1158,30 @@ function get_columns_detail(var_id, value_columns){ }; columns.push(time); + // Define the time_lapse_status column and add it to the columns array var time_lapse_status = { field:'time_lapse_status', visible: false, }; columns.push(time_lapse_status); + // Loop through the detail table value columns for (const c of detail_table_value_columns) { + // Define the value column and add it to the columns array var value_column = { field: c, title: c.charAt(0).toUpperCase() + c.slice(1), cellStyle: style_detail_value_error, -// formatter: format_value, footerFormatter: footer_average }; + // If the column is 'sum', update the cellStyle and footerFormatter of the value column if ( c == 'sum'){ value_column.cellStyle = style_detail_value_error; value_column.footerFormatter = footer_sum; } columns.push(value_column); + // Define the value_column_error column and add it to the columns array var value_column_error = { field: c + '_error', visible: false, @@ -875,7 +1189,7 @@ function get_columns_detail(var_id, value_columns){ columns.push(value_column_error); } - + // Define the outlier_err column and add it to the columns array var outlier_err = { field:'stddev_error', title:'Outliers', @@ -885,7 +1199,7 @@ function get_columns_detail(var_id, value_columns){ }; columns.push(outlier_err); - + // Define the value_difference column and add it to the columns array var value_difference = { field:'value_difference', title:'Value diff.', @@ -894,12 +1208,14 @@ function get_columns_detail(var_id, value_columns){ }; columns.push(value_difference); + // Define the value_diff_error column and add it to the columns array var value_diff_error = { field:'value_difference_error', visible: false, }; columns.push(value_diff_error); + // Define the action column and add it to the columns array var action = { field: 'action', title: 'Action', @@ -910,83 +1226,147 @@ function get_columns_detail(var_id, value_columns){ }; columns.push(action); + // Return the columns array return columns } +/** + * This function generates the action buttons for each row in the daily table. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {string} The HTML string for the action buttons. + */ function operate_table_daily(value, row, index) { - return [ - '', - '', - ' ', - ].join('') + // Return the HTML string for the action buttons + // The 'search' button is used to view the details of the row + return [ + '', + '', + ' ', + ].join('') } +/** + * This function generates the action buttons for each row in the detail table. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {string} The HTML string for the action buttons. + */ function operate_table_detail(value, row, index) { - return [ - '', - '', - ' ', - ].join('') + // Return the HTML string for the action buttons + // The 'update' button is used to modify the details of the row + return [ + '', + '', + ' ', + ].join('') } - - - +/** + * This function styles a row in the table based on its state. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {Object} The class to apply to the row. + */ function style_row(row, index){ - var _class = ''; - if (row.state == false) { - _class = 'error'; - } + // Initialize an empty string for the class + var _class = ''; - else - _class = ''; - return {classes: _class} + // If the state of the row is false, set the class to 'error' + if (row.state == false) { + _class = 'error'; + } + // Otherwise, keep the class as an empty string + else { + _class = ''; + } -// var _class = ''; -// if (row.is_selected) { -// _class = 'normal'; -// } -// return {classes: _class} + // Return the class to apply to the row + return {classes: _class} + // The following commented out code would set the class to 'normal' if the row is selected + // var _class = ''; + // if (row.is_selected) { + // _class = 'normal'; + // } + // return {classes: _class} } +/** + * This function styles the id column in the table based on the row's validation status. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {Object} The class to apply to the cell. + */ function style_id(value, row, index){ + // Initialize an empty string for the class var _class = ''; + // If all the data in the row is validated, set the class to 'validated' if (row.all_validated == true){ _class = 'validated'; } - // TODO check row.seleccionado translation + // If the row is not selected (assuming 'seleccionado' means 'selected' in Spanish), set the class to 'error' else if (row.seleccionado == false){ _class = 'error'; } + // If neither of the above conditions are met, keep the class as an empty string else{ _class = ''; } + + // Return the class to apply to the cell return { classes: _class} } +/** + * This function styles the date column in the table based on the row's date error status. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {Object} The class to apply to the cell. + */ function style_date(value, row, index){ + // Initialize an empty string for the class var _class = ''; - if (row.date_error > 0) + + // If the date error status of the row is greater than 0, set the class to 'error' + if (row.date_error > 0){ _class = 'error'; - else + } + // Otherwise, keep the class as an empty string + else{ _class = ''; + } + + // Return the class to apply to the cell return { classes: _class} } +/** + * This function styles the percentage column in the table based on the row's percentage error status. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {Object} The class to apply to the cell. + */ function style_percentage(value, row, index) { + // If the percentage error status of the row is true, set the class to 'error' if (row.percentage_error == true) { return { classes: 'error' } } + // Otherwise, set the class to 'normal' else{ return { classes: 'normal' @@ -995,82 +1375,165 @@ function style_percentage(value, row, index) { } +/** + * This function styles the value column in the table based on the row's suspicious count for the field. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @param {string} field - The field to check for suspicious count. + * @returns {Object} The class to apply to the cell. + */ function style_value(value, row, index, field){ + // Initialize an empty string for the class var _class = ''; - field_value = "suspicious_" + field +"s_count"; + // Create the field value by appending "suspicious_" and "s_count" to the field + var field_value = "suspicious_" + field +"s_count"; + + // If the suspicious count for the field in the row is greater than 0, set the class to 'error' if (row[field_value]>0 ) _class = 'error'; + // Otherwise, set the class to 'normal' else _class = 'normal'; + + // Return the class to apply to the cell return { classes: _class} } +/** + * This function styles the value difference column in the table based on the row's value difference error count. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {Object} The class to apply to the cell. + */ function style_value_diff(value, row, index){ - var _class = '' + // Initialize an empty string for the class + var _class = ''; + + // If the value difference error count of the row is greater than or equal to 1, set the class to 'error' if (row.value_difference_error_count >= 1) _class = 'error'; + // Otherwise, keep the class as an empty string else _class = ''; + + // Return the class to apply to the cell return { classes: _class} } - +/** + * This function styles the detail time column in the table based on the row's time lapse status and selection status. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {Object} The class to apply to the cell. + */ function style_detail_time(value, row, index){ + // Initialize an empty string for the class var _class = ''; - if (row.time_lapse_status == 0){ + + // If the time lapse status of the row is 0 or any other value not covered by the conditions, set the class to 'error' + if (row.time_lapse_status == 0 || row.time_lapse_status != 1 && row.time_lapse_status != 2){ _class = 'error'; - }else if (row.time_lapse_status == 2){ + } + // If the time lapse status of the row is 2, set the class to 'warning' + else if (row.time_lapse_status == 2){ _class = 'warning'; - }else if (row.time_lapse_status == 1){ + } + // If the time lapse status of the row is 1 and the row is selected, set the class to 'normal' + else if (row.time_lapse_status == 1){ if (row.is_selected){ _class = 'normal'; } - }else { - _class = 'error'; } + + // Return the class to apply to the cell return { classes: _class} } +/** + * This function styles the detail value error column in the table based on the row's field error status and selection status. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @param {string} field - The field to check for error status. + * @returns {Object} The class to apply to the cell. + */ function style_detail_value_error(value, row, index, field){ + // Initialize an empty string for the class var _class = ''; - field_error = field + '_error'; + + // Create the field error by appending "_error" to the field + var field_error = field + '_error'; + + // If the field error status of the row is true, set the class to 'error' if (row[field_error] === true){ _class = 'error'; } + // If the row is selected, set the class to 'normal' else if (row.is_selected){ _class = 'normal'; } + + // Return the class to apply to the cell return { classes: _class}; } +/** + * This function styles the standard deviation error column in the table based on the cell's value and the row's selection status. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {Object} The class to apply to the cell. + */ function style_stddev_err(value, row, index){ + // Initialize an empty string for the class var _class = ''; + + // If the value of the cell is true, set the class to 'error' if (value === true) { _class = 'error'; } + // If the row is selected, set the class to 'normal' else if (row.is_selected){ _class = 'normal'; } + + // Return the class to apply to the cell return { classes: _class} } +/** + * This function styles the detail value difference column in the table based on the row's value difference error status and selection status. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @returns {Object} The class to apply to the cell. + */ function style_detail_value_diff(value, row, index){ + // Initialize an empty string for the class var _class = ''; + + // If the value difference error status of the row is true, set the class to 'error' if (row.value_difference_error) { _class = 'error'; } + // If the row is selected, set the class to 'normal' else if (row.is_selected){ _class = 'normal'; } + + // Return the class to apply to the cell return { classes: _class} } @@ -1080,62 +1543,118 @@ function style_detail_value_diff(value, row, index){ +/** + * This function formats the value of a cell in the table based on the field and the row's error status. + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @param {string} field - The field to check for error status. + * @returns {string} The formatted content to display in the cell. + */ function format_value(value, row, index, field){ + // Initialize a span for the badge with a placeholder for the number of errors var span = 'num'; + // Initialize the content with the value of the cell var content = value; var errors_field; + + // If the field is one of the specified fields, set the errors_field to the suspicious count for the field if (["sum", "average", "value", "maximum", "minimum"].includes(field)){ errors_field = "suspicious_" + field +"s_count"; - }else if (field == "value_difference_error_count"){ + } + // If the field is "value_difference_error_count", set the errors_field to "value_difference_error_count" and clear the content + else if (field == "value_difference_error_count"){ errors_field = "value_difference_error_count"; content = ""; - }else{ + } + // Otherwise, set the errors_field to the error status for the field + else{ errors_field = field +"_error"; } + // If the error count for the errors_field in the row is greater than 0, add the badge to the content if (row[errors_field]>0 ){ span = span.replace('num',row[errors_field].toString()); content = content + ' ' + span; } + + // Return the formatted content return content } +/** + * This function formats the standard deviation error cell in the table. + * If the value is truthy, it returns "X", otherwise it returns "-". + * @param {Object} value - The value of the cell. + * @param {Object} row - The data of the row. + * @param {number} index - The index of the row. + * @param {string} field - The field to check for error status. + * @returns {string} The formatted content to display in the cell. + */ function format_stddev_err(value, row, index, field){ + // If the value is truthy, return "X" if (value){ return "X"; } + // Otherwise, return "-" return "-"; } +/** + * This function generates a footer id for a table. + * It counts the number of rows where 'state' is truthy and 'is_selected' is false, and displays this count in a badge. + * @param {Array} data - The data of the table. + * @returns {string} The footer id to display in the table. + */ function footer_id(data){ + // Initialize a span for the badge with a placeholder for the number var span = 'num'; + + // Use the reduce function to count the number of rows where 'state' is truthy and 'is_selected' is false var num_date = data.reduce(function(num, i){ - // TODO check translation: seleccionado + // If 'state' is truthy and 'is_selected' is false, increment the count if (i['state'] && i['is_selected']==false) return num + 1; + // Otherwise, keep the count the same else return num; }, 0); + // Replace the placeholder in the span with the count span = span.replace('num',num_date.toString()); + // Return the span to display in the table return span; - } +/** + * This function generates a footer for a table that displays the number of unique dates and the total number of days. + * It also displays a badge with the number of rows where 'date_error' is greater than 0. + * @param {Array} data - The data of the table. + * @returns {string} The footer to display in the table. + */ function footer_date(data){ + // Initialize a span for the badge with a placeholder for the number var span = 'num'; + + // Get the selected variable id var var_id = $("#id_variable").val(); + + // Initialize an empty array for the dates var dates = []; + // Use the map function to add each row's date to the dates array $.map(data, function(row){ dates.push(row.date); }); + // Count the number of unique dates var sum = dates.unique().length; + + // Use the reduce function to count the number of rows where 'date_error' is greater than 0 var num_date = data.reduce(function(num, i){ if (i['date_error']>0) return num +1; @@ -1143,27 +1662,40 @@ function footer_date(data){ return num; }, 0); - // TODO ask the team for prefferred behaviour + // Replace the placeholder in the span with the count span = span.replace('num', num_date.toString()); + // Return the sum of unique dates, the total number of days, and the span to display in the table return sum + ' of ' + indicators_daily['num_days'] + ' days ' + span; } +/** + * This function generates a footer for a table that displays the average of a field and a badge with the number of errors. + * @param {Array} data - The data of the table. + * @returns {string} The footer to display in the table. + */ function footer_average(data){ + // Get the field from the context of the function var field = this.field; - var field_error = ''; - - field_error = field + '_error'; + // Create the field error by appending "_error" to the field + var field_error = field + '_error'; + // Initialize a span for the badge with a placeholder for the number var span = 'num'; + + // Initialize the mean and the sum var mean = 0; + + // Use the reduce function to calculate the sum of the field for rows where 'state' is truthy and the field is not null or empty var sum = data.reduce(function (sum, i) { - if (i['state'] && i[field] != null && i[field] != "") + if (i['state'] && i[field] != null && i[field] != "") return sum + parseFloat(i[field]) else return sum; }, 0); + + // Use the reduce function to count the number of rows where 'state' is truthy and the field is not null or empty var data_count = data.reduce(function (sum, i) { if (i['state'] && i[field] != null && i[field] != "") return sum + 1; @@ -1171,6 +1703,7 @@ function footer_average(data){ return sum; }, 0); + // Use the reduce function to count the number of rows where the field error is truthy and 'state' is truthy var num_value = data.reduce (function (num, i){ if (i[field_error] && i['state']) return num + 1; @@ -1178,22 +1711,38 @@ function footer_average(data){ return num; }, 0); + // Replace the placeholder in the span with the count span = span.replace('num', num_value); + // If the sum is not a number, set the mean to "-" if (isNaN(sum)) mean = '-'; + // Otherwise, calculate the mean by dividing the sum by the count and rounding to 2 decimal places else mean = (sum / data_count).toFixed(2); + + // Return the mean and the span to display in the table return mean + ' ' + span; } +/** + * This function generates a footer for a table that displays the sum of a field and a badge with the number of errors. + * @param {Array} data - The data of the table. + * @returns {string} The footer to display in the table. + */ function footer_sum(data){ + // Get the field from the context of the function var field = this.field; + // Create the field error by appending "_error" to the field var field_error = this.field + '_error'; + + // Initialize a span for the badge with a placeholder for the number var span = 'num'; + + // Use the reduce function to calculate the sum of the field for rows where 'state' is truthy and the field is not null or empty var sum = data.reduce(function (sum, i) { if (i['state'] && i[field] != null && i[field] != "" ){ return sum + parseFloat(i[field]) @@ -1201,8 +1750,9 @@ function footer_sum(data){ else{ return sum } - }, 0); + + // Use the reduce function to count the number of rows where the field error is truthy and 'state' is truthy var num_value = data.reduce (function (sum, i){ if (i[field_error] && i['state']) return sum +1 ; @@ -1210,31 +1760,50 @@ function footer_sum(data){ return sum; }, 0); + // Replace the placeholder in the span with the count span = span.replace('num', num_value); + // Return the sum (rounded to 2 decimal places) and the span to display in the table return sum.toFixed(2) + ' ' + span; } +/** + * This function generates a footer for a table that displays a badge with the count of rows where 'value_difference_error_count' is greater than or equal to 1. + * @param {Array} data - The data of the table. + * @returns {string} The footer to display in the table. + */ function footer_value_diff(data){ - + // Initialize a span for the badge with a placeholder for the number var span = 'num'; - var num_vd= data.reduce(function(num, i){ + + // Use the reduce function to count the number of rows where 'value_difference_error_count' is greater than or equal to 1 + var num_vd = data.reduce(function(num, i){ if (i['value_difference_error_count'] >= 1 ) return num + 1; else return num; }, 0); - span = span.replace('num',num_vd); + // Replace the placeholder in the span with the count + span = span.replace('num', num_vd); + + // Return the span to display in the table return span; } +/** + * This function generates a footer for a table that displays the count of rows where 'state' is truthy and a badge with the count of rows where 'time_lapse_status' is 0, 2, or 3. + * @param {Array} data - The data of the table. + * @returns {string} The footer to display in the table. + */ function footer_data_count(data){ + // Initialize a span for the badge with a placeholder for the number var span = 'num'; + // Use the reduce function to count the number of rows where 'state' is truthy var sum = data.reduce(function (sum, i) { if (i['state']){ return sum + 1 @@ -1242,9 +1811,9 @@ function footer_data_count(data){ else{ return sum } - }, 0); + // Use the reduce function to count the number of rows where 'time_lapse_status' is 0, 2, or 3 var num_date = data.reduce(function(num, i){ // TODO check logic if ( (i['time_lapse_status']==0) || (i['time_lapse_status']==2) || (i['time_lapse_status']==3)) @@ -1253,15 +1822,25 @@ function footer_data_count(data){ return num; }, 0); + // Replace the placeholder in the span with the count span = span.replace('num',num_date.toString()); + // Return the count of rows where 'state' is truthy, the total number of data, and the span to display in the table return sum + ' of ' + indicators_subhourly['num_data'] + '. ' + span; } +/** + * This function generates a footer for a table that displays a badge with the count of rows where 'stddev_error' is truthy and 'state' is also truthy. + * @param {Array} data - The data of the table. + * @returns {string} The footer to display in the table. + */ function footer_stddev_err(data){ + // Initialize a span for the badge with a placeholder for the number var span = 'num'; + + // Use the reduce function to count the number of rows where 'stddev_error' is truthy and 'state' is also truthy var num_stddev = data.reduce(function(num, i){ if (i['stddev_error'] && i['state']) return num +1; @@ -1269,13 +1848,23 @@ function footer_stddev_err(data){ return num; }, 0); + // Replace the placeholder in the span with the count span = span.replace('num',num_stddev.toString()); + // Return the span to display in the table return span; } +/** + * This function generates a footer for a table that displays a badge with the count of rows where 'value_difference_error' is truthy. + * @param {Array} data - The data of the table. + * @returns {string} The footer to display in the table. + */ function footer_detail_value_diff(data){ + // Initialize a span for the badge with a placeholder for the number var span = 'num'; + + // Use the reduce function to count the number of rows where 'value_difference_error' is truthy var num_vde = data.reduce(function(num, i){ if (i['value_difference_error']) return num + 1; @@ -1283,8 +1872,10 @@ function footer_detail_value_diff(data){ return num; }, 0); + // Replace the placeholder in the span with the count span = span.replace('num', num_vde.toString()); + // Return the span to display in the table return span; } @@ -1293,113 +1884,190 @@ function footer_detail_value_diff(data){ +/** + * This function returns a filter based on the provided option. + * @param {string} option - The option to determine the filter. + * @returns {Array} The filter to be used. + */ function get_filter_time(option){ + // Initialize an empty filter array var filter = []; + // If the option is 'shorter_than_tx_period', set the filter to [0] if (option == 'shorter_than_tx_period') filter = [0]; + // If the option is 'greater_than_tx_period', set the filter to [2] else if (option == 'greater_than_tx_period') filter = [2]; + // For any other option, set the filter to [0, 1, 2] else filter = [0, 1, 2]; - return filter + + // Return the filter + return filter; } +/** + * This function returns a filter based on the provided option. + * @param {string} option - The option to determine the filter. + * @returns {Array} The filter to be used. + */ function get_filter_percentage(option){ + // Initialize an empty filter array var filter = []; + // If the option is 'error', set the filter to [true] if (option == 'error') filter = [true]; + // If the option is 'normal', set the filter to [false] else if (option == 'normal') filter = [false]; + // For any other option, set the filter to [true, false] else filter = [true, false]; + // Return the filter return filter; } +/** + * This function returns a filter based on the provided option. + * @param {string} option - The option to determine the filter. + * @returns {Array} The filter to be used. + */ function get_filter_value(option){ + // Initialize an empty filter array var filter = []; + // If the option is 'error', set the filter to [true] if (option == 'error') filter = [true]; + // If the option is 'normal', set the filter to [false] else if (option == 'normal') filter = [false]; + // For any other option, set the filter to [true, false, null] else filter = [true, false, null]; - return filter + // Return the filter + return filter; } +/** + * This function returns a filter based on the provided option. + * @param {string} option - The option to determine the filter. + * @returns {Array} The filter to be used. + */ function get_filter_stddev(option){ + // Initialize an empty filter array var filter = []; + // If the option is 'error', set the filter to [true] if (option == 'error') filter = [true]; + // If the option is 'normal', set the filter to [false] else if (option == 'normal') filter = [false]; + // For any other option, set the filter to [true, false, null] else filter = [true, false, null]; - return filter + // Return the filter + return filter; } -function get_filter_state(option){ +/** + * This function returns a filter based on the provided option. + * @param {string} option - The option to determine the filter. + * @returns {Array} The filter to be used. + */ +function get_filter_stddev(option){ + // Initialize an empty filter array var filter = []; + // If the option is 'error', set the filter to [true] if (option == 'error') - filter = [false]; - else if (option == 'normal') filter = [true]; + // If the option is 'normal', set the filter to [false] + else if (option == 'normal') + filter = [false]; + // For any other option, set the filter to [true, false, null] else - filter = [true, false]; + filter = [true, false, null]; - return filter + // Return the filter + return filter; } +/** + * This function returns a filter based on the provided option. + * @param {string} option - The option to determine the filter. + * @returns {Array} The filter to be used. + */ function get_filter_selected(option){ + // Initialize an empty filter array var filter = []; + // If the option is 'selected', set the filter to [true] if (option == 'selected') filter = [true]; + // If the option is 'non-selected', set the filter to [false] else if (option == 'non-selected') filter = [false]; + // For any other option, set the filter to [true, false] else filter = [true, false]; - return filter + // Return the filter + return filter; } +/** + * This function returns a filter based on the provided option. + * @param {string} option - The option to determine the filter. + * @returns {Array} The filter to be used. + */ function get_filter_value_difference(option){ + // Initialize an empty filter array var filter = []; + // If the option is 'error', set the filter to [true] if (option == 'error') filter = [true]; + // If the option is 'normal', set the filter to [false] else if (option == 'normal') filter = [false]; + // For any other option, set the filter to [true, false, null] else filter = [true, false, null]; + // Return the filter return filter; } +/** + * This function filters the detail table based on the selected options from the user interface. + */ function filter_detail_table(){ + // Get the selected options from the user interface var time = $("#chk_detail_time").val(); var value = $("#chk_detail_value").val(); var stddev = $("#chk_detail_stddev").val(); var selected = $("#chk_detail_selected").val(); var value_difference = $("#chk_detail_value_difference").val(); + // Get the filters based on the selected options var filter_time = get_filter_time(time); var filter_value = get_filter_value(value); var filter_stddev = get_filter_stddev(stddev); var filter_selected = get_filter_selected(selected); var filter_value_difference = get_filter_value_difference(value_difference); + // Create the filter object var filter = { is_selected: filter_selected, stddev_error: filter_stddev, @@ -1407,51 +2075,80 @@ function filter_detail_table(){ value_difference_error: filter_value_difference, }; + // Add the value filter to the main columns var main_column = detail_table_value_columns.filter(c => ["sum", "average", "value"].includes(c)); main_column.forEach(c => filter[c + '_error'] = filter_value); + + // Apply the filter to the table $("#table_detail").bootstrapTable('filterBy', filter); } +/** + * This function resets the filters based on the provided type. + * @param {string} type - The type of filters to reset. + */ function clean_filters(type){ + // If the type is 'detail', reset the detail filters if (type == 'detail'){ + // Reset the time filter $("#chk_detail_time").prop('selectedIndex',0); + // Reset the value filter $("#chk_detail_value").prop('selectedIndex',0); + // Reset the stddev filter $("#chk_detail_stddev").prop('selectedIndex',0); + // Reset the selected filter $("#chk_detail_selected").prop('selectedIndex',0); + // Reset the value difference filter $("#chk_detail_value_difference").prop('selectedIndex',0); + // Reset the selection text $("#txt_detail_selection").val(''); } else{ - // for daily filters + // For daily filters, the reset logic would go here } } +/** + * This function checks if a given value is within a specified range. + * @param {number} value - The value to check. + * @returns {boolean} Returns true if the value is outside the range, false otherwise. + */ function get_value_error(value){ + // Get the minimum and maximum values from the user interface var minimum = Number($("#id_minimum").val()); var maximum = Number($("#id_maximum").val()); + + // Initialize the error flag as false var value_error = false; + // If the value is greater than the maximum or less than the minimum, set the error flag to true if (Number(value) > maximum || Number(value) < minimum ) { value_error = true; } + + // Return the error flag return value_error; } +/** + * This function enables or disables the "New" button based on the selected variable ID. + */ function enable_new(){ + // Get the selected variable ID from the user interface var variable_id = $("#id_variable").val(); + // If the variable ID is not "11" or is "4" or "5", enable the "New" button if ( variable_id != "11" || variable_id == "4" || variable_id == "5" ) $("#btn_detail_new").attr("disabled", false); + // Otherwise, disable the "New" button else $("#btn_detail_new").attr("disabled", true); } - - Array.prototype.unique=function(a){ return function(){return this.filter(a)}}(function(a,b,c){return c.indexOf(a,b+1)<0 });