diff --git a/ldvspec/lofardata/templates/lofardata/specification.html b/ldvspec/lofardata/templates/lofardata/specification.html index f9eced82e8bb8ebbea256c333b4d45b87b5b704c..f4c4ee8873d779206d2be8a982769d14cdc383b0 100644 --- a/ldvspec/lofardata/templates/lofardata/specification.html +++ b/ldvspec/lofardata/templates/lofardata/specification.html @@ -162,9 +162,21 @@ {% elif specification.is_ready == False %} <div class="row"><h4>Loading...</h4></div> {% else %} - <label for="inputs_result"><h4>Inputs:</h4></label> - {{ specification.inputs|json_script:"inputs" }} - {{ form.inputs|as_crispy_field }} + <div class="row"> + <div class="col"> + <label for="total_size"><h5>Total size: </h5></label> + <span id="total_size">{{ total_file_size }}</span> + </div> + <div class="col"> + <label for="size_per_task"><h5>Size per task: </h5></label> + <span id="size_per_task">{{ average_file_size_raw }}</span> + </div> + </div> + <div> + <label for="inputs_result"><h4>Inputs:</h4></label> + {{ specification.inputs|json_script:"inputs" }} + {{ form.inputs|as_crispy_field }} + </div> {% endif %} <div class="row bottom-bar"> <div class="col-2"> @@ -214,6 +226,16 @@ }) } + function format_size(num) { + const suffixes = ["", "K", "M", "G", "T", "P", "E", "Z"] + for (let i=0; i<suffixes.length; i++){ + if( Math.abs(num) <= Math.pow(1000.0, i+1) ) { + let value = Math.abs(num)/Math.pow(1000., i) + return value.toFixed(2) + ' ' + suffixes[i] + 'B'; + } + } + return null + } function delay(time) { return new Promise(resolve => setTimeout(resolve, time)); } @@ -233,7 +255,7 @@ updateWorkflows(selectedProcessingSite); } - function set_value_of_filters(item) { + function setValueOfFilters(item) { const value = item.value; const name = item.name; const query_type = item.getAttribute('data-filter') @@ -250,7 +272,7 @@ } function changeFilter(event) { - set_value_of_filters(event.currentTarget); + setValueOfFilters(event.currentTarget); } function attachSignals() { @@ -269,6 +291,8 @@ delay(3000).then(() => location.reload(true)); } + + $("#id_batch_size").on('change', updateSizePerTask) } function formatJSONField(field_name) { @@ -280,11 +304,41 @@ } } + function updateSizePerTask(){ + const number_of_files = {{ number_of_files }}; + let batchSize = $('#id_batch_size').val(); + + if(batchSize > number_of_files){ + batchSize = number_of_files; + $('#id_batch_size').val(batchSize) + }else if(batchSize < 0){ + batchSize = 0; + $('#id_batch_size').val(batchSize) + } + const totalFileSize = {{ total_file_size_raw }}; + const averageFileSize = {{ average_file_size_raw }}; + if(batchSize == 0) { + console.log('{{ total_file_size }}') + $('#size_per_task').text('{{ total_file_size }}') + } + else{ + const sizePerTask = averageFileSize * batchSize; + $('#size_per_task').text(sizePerTask); + formatDataSizes('#size_per_task') + } + + } + + function formatDataSizes(fieldName){ + $(fieldName).text(format_size($(fieldName).text())); + } + function onReady() { attachSignals(); updateWorkflows($("#id_processing_site")[0].value); formatJSONField('#id_inputs') formatJSONField('#id_filters') + updateSizePerTask() } $(document).ready(onReady); diff --git a/ldvspec/lofardata/tests/test_util_funcs.py b/ldvspec/lofardata/tests/test_util_funcs.py index 300db5501e26386839423b9a0815a59877615d29..59bd6363f82e8900cffd34a8f415f3a9ac2e06bc 100644 --- a/ldvspec/lofardata/tests/test_util_funcs.py +++ b/ldvspec/lofardata/tests/test_util_funcs.py @@ -1,7 +1,7 @@ import unittest from lofardata.tasks import split_entries_to_batches - +from lofardata.views import compute_size_of_inputs class SplitEntries(unittest.TestCase): def test_no_splitting(self): @@ -25,3 +25,18 @@ class SplitEntries(unittest.TestCase): res = split_entries_to_batches([*range(10)], 3) self.assertEqual(len(res), 4) self.assertEqual(len(res[-1]), 1) + + +class ComputeInputSizes(unittest.TestCase): + def test_input_sizes(self): + test_data = { + 'only_a_file': {'class': 'File', 'size': 1}, + 'a_list_of_files': [{'class': 'File', 'size': 1}, {'class': 'File', 'size': 1}], + 'nested_files': [{'item1': {'class': 'File', 'size': 1}}, {'class': 'File', 'size': 1}], + 'not_a_file': 'bla' + } + result, number_of_files = compute_size_of_inputs(test_data) + + self.assertEqual(5, result) + self.assertEqual(5, number_of_files) + diff --git a/ldvspec/lofardata/views.py b/ldvspec/lofardata/views.py index 2525a1bfbf09107cb7db39a34cc5ebbcc88b1412..beca41abd059a23bcd7610c0799e297fceeecded 100644 --- a/ldvspec/lofardata/views.py +++ b/ldvspec/lofardata/views.py @@ -1,3 +1,4 @@ +import humanize from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist from django.shortcuts import redirect @@ -17,6 +18,24 @@ from .serializers import DataProductSerializer, \ from .tasks import insert_task_into_atdb +def compute_size_of_inputs(inputs: dict) -> (int, int): + total_size = 0 + number_of_files = 0 + + if isinstance(inputs, dict) and 'size' in inputs: + total_size = inputs['size'] + number_of_files = 1 + elif isinstance(inputs, dict) or isinstance(inputs, list) or isinstance(inputs, tuple): + values = inputs + if isinstance(inputs, dict): + values = inputs.values() + for value in values: + item_total, item_count = compute_size_of_inputs(value) + total_size += item_total + number_of_files += item_count + return total_size, number_of_files + + class DynamicFilterSet(filters.FilterSet): class Meta: filter_class = None @@ -120,9 +139,18 @@ def specification_view(request, pk=None): dataproduct_filters = preprocess_filters_specification_view(specification) + total_input_size, number_of_files = compute_size_of_inputs(specification.inputs) + + average_file_size = total_input_size / number_of_files if number_of_files else 0 + return render(request, "lofardata/specification.html", { 'form': form, 'specification': specification, + 'total_file_size': humanize.naturalsize(total_input_size), + 'total_file_size_raw': total_input_size, + 'number_of_files': number_of_files, + 'average_file_size': humanize.naturalsize(average_file_size), + 'average_file_size_raw': average_file_size, 'specifications_list': specifications_list, 'filters': dataproduct_filters }) diff --git a/ldvspec/requirements/base.txt b/ldvspec/requirements/base.txt index 7fefc133b803df987ff6a7bce0e5673ea7e230f9..831a959ebebdf244153aa77b4fa1f380e410a2a2 100644 --- a/ldvspec/requirements/base.txt +++ b/ldvspec/requirements/base.txt @@ -14,4 +14,5 @@ uritemplate==4.1.1 sshtunnel==0.4.0 django-uws==0.2.dev355575 django-crispy-forms==1.14.0 - crispy-bootstrap5==0.6 \ No newline at end of file +crispy-bootstrap5==0.6 +humanize==4.4.0 \ No newline at end of file