diff --git a/README.md b/README.md index 43dc2c43513d3492815ef7fa4bfe48319beb05ae..444b971a0eb184be81f7e81f1c634bc418454971 100644 --- a/README.md +++ b/README.md @@ -126,8 +126,4 @@ This is the procedure for that. > docker exec -it atdb-ldv python manage.py migrate --settings atdb.settings.docker_sdc -## Original ATDB Context diagrams -Similar diagram for ATDB-LDV will follow, showing the relationships with external services - - diff --git a/atdb/atdb/settings/base.py b/atdb/atdb/settings/base.py index d536e05e95822970d092061d29410e820c769476..d56eb4856aabe287e9c27b747562cb79b247517d 100644 --- a/atdb/atdb/settings/base.py +++ b/atdb/atdb/settings/base.py @@ -192,3 +192,4 @@ ALL_STATUSSES = ['defining','defined','staging','staged','processing','processed ACTIVE_STATUSSES = ['staging','staged','processing','processed','validated', 'scrubbing','scrubbed','archiving','archived'] STATUSSES_WITH_DATA = ['staged','processing','processed','validated', 'scrubbing','scrubbed','archiving','archived'] +QUERY_LIMIT_MULTI_CHANGE = 1000 \ No newline at end of file diff --git a/atdb/taskdatabase/services/algorithms.py b/atdb/taskdatabase/services/algorithms.py index 8f69e5bbe703effe0df71a96c49260ff773ff53a..58fb0c824b2e75d87d9368325eb8813aaae0f1d9 100644 --- a/atdb/taskdatabase/services/algorithms.py +++ b/atdb/taskdatabase/services/algorithms.py @@ -205,7 +205,7 @@ def aggregate_resources_logs(): return records -def construct_link(request, status, workflow_id, count): +def construct_link_to_tasks_api(request, status, workflow_id, count): link = str(count) try: query = "?status=" + status + "&workflow__id=" + str(workflow_id) @@ -223,6 +223,25 @@ def construct_link(request, status, workflow_id, count): return link +def construct_link_to_workflow_api(request, workflow_result): + title = str(workflow_result['id']) + ' - ' + str(workflow_result['name']) + link = str(title) + try: + if settings.DEV == True: + url = request.build_absolute_uri('/atdb/workflows/') + str(workflow_result['id']) + else: + # Unclear why 'build_absolute_uri' doesn't return 'https' in production. + # Probably because the https is handled fully outside the container by Traefik + # and ATDB is not aware of that. + + url = "https://" + request.get_host() + '/atdb/workflows/' + str(workflow_result['id']) + + link = '<a href="' + url + '" target="_blank">' + title + "</a>" + except: + pass + return link + + def human_readable(size_in_bytes): for count in ['Bytes', 'KB', 'MB', 'GB', 'TB']: if size_in_bytes > -1024.0 and size_in_bytes < 1024.0: @@ -249,7 +268,9 @@ def construct_tasks_per_workflow_html(request, workflow_results): d = workflow_result['nr_of_tasks_per_status'] #values = "<td><b>" + str(workflow_result['id'])+" - "+workflow_result['name'] + "</b></td>" - values = "<td><b>" + str(workflow_result['id']) + "</b></td>" + # values = "<td><b>" + str(workflow_result['id']) + "</b></td>" + link = construct_link_to_workflow_api(request, workflow_result) + values = "<td><b>" + link + "</b></td>" for key in d: percentage = round(int(d[key]) / int(workflow_result['nr_of_tasks']) * 100) @@ -260,7 +281,7 @@ def construct_tasks_per_workflow_html(request, workflow_results): style = "active" # bonus: add a query link - link = construct_link(request, key, workflow_result['id'], d[key]) + link = construct_link_to_tasks_api(request, key, workflow_result['id'], d[key]) values += "<td class=" + style + ">" + str(percentage) + "% (" + link + ")</td>" # add sizes diff --git a/atdb/taskdatabase/tables.py b/atdb/taskdatabase/tables.py index 44e62816601647fe0a8f0ca6ae58fca5d650f640..3074148d6eec9f13fc3a6384eded1144d16dd347 100644 --- a/atdb/taskdatabase/tables.py +++ b/atdb/taskdatabase/tables.py @@ -2,6 +2,24 @@ import django_tables2 as tables from .models import Task +# render the StatusColumn based on the style.css +class IDColumn(tables.Column): + attrs = { + "td": { + "class": lambda record: record.status + }, + "tr": { + "class": lambda record: record.status + }, + "a": { + "href" : lambda record: record.get_absolute_url, + "target": "_blank" + } + } + def render(self, record): + link = '<a href="{{ record.get_absolute_url }}" target="_blank">{{ record.id }} </a>' + return "{}".format(link) + # render the StatusColumn based on the style.css class StatusColumn(tables.Column): attrs = { @@ -21,12 +39,18 @@ class TaskTable(tables.Table): class Meta: model = Task template_name = "django_tables2/bootstrap4.html" - fields = ("id", "workflow","priority", "status","project","sas_id","buttons") + fields = ("id", "workflow","priority", "status","project","sas_id","actions","buttons") # columns that need specific rendering + # id = IDColumn() (too complicated, finish later.. if time status = StatusColumn() + #creationtime = tables.Column(verbose_name='CreationTime') + actions = tables.TemplateColumn(verbose_name='Details', + template_name='query/action_buttons_per_row.html', + orderable=False) # orderable not sortable + buttons = tables.TemplateColumn(verbose_name='Set Status', template_name='query/status_buttons_per_row.html', orderable=False) # orderable not sortable diff --git a/atdb/taskdatabase/templates/query/action_buttons_per_row.html b/atdb/taskdatabase/templates/query/action_buttons_per_row.html new file mode 100644 index 0000000000000000000000000000000000000000..9447dc1f47d5db8e52920adac9c109504708fa05 --- /dev/null +++ b/atdb/taskdatabase/templates/query/action_buttons_per_row.html @@ -0,0 +1,2 @@ + <a href="{% url 'task-details' record.pk 0 %}" class="btn btn-primary btn-sm" role="button"><i class="fas fa-list"></i> Details</a> + diff --git a/atdb/taskdatabase/templates/query/tables2.html b/atdb/taskdatabase/templates/query/query.html similarity index 100% rename from atdb/taskdatabase/templates/query/tables2.html rename to atdb/taskdatabase/templates/query/query.html diff --git a/atdb/taskdatabase/templates/query/set_multi_status_panel.html b/atdb/taskdatabase/templates/query/set_multi_status_panel.html index 008b331d9250704db4ad79ad53ea3cdde7ad0f58..00cb2f624915152dd7300db1c9bae64e09f39ca8 100644 --- a/atdb/taskdatabase/templates/query/set_multi_status_panel.html +++ b/atdb/taskdatabase/templates/query/set_multi_status_panel.html @@ -4,18 +4,18 @@ <div class="card"> <div class="card-body"> - {% if user.is_superuser %} + {% if user.is_superuser %} <p>Click to change Status for all these tasks</p> <table> <tr> - <a href="{% url 'task-setstatus' 1 'defining' '1' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> defining</a> - <a href="{% url 'task-setstatus' 1 'staged' '1' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> staged</a> - <a href="{% url 'task-setstatus' 1 'processed' '1' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> processed</a> - <a href="{% url 'task-setstatus' 1 'validated' '1' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> validated</a> - <a href="{% url 'task-setstatus' 1 'scrubbed' '1' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> scrubbed</a> - <a href="{% url 'task-setstatus' 1 'archived' '1' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> archived</a> - - <a href="{% url 'task-setstatus' 1 'finished' '1' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> finished</a> + <a href="{% url 'task-multi-setstatus' 'defining' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> defining</a> + <a href="{% url 'task-multi-setstatus' 'defined' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> defined</a> + <a href="{% url 'task-multi-setstatus' 'staged' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> staged</a> + <a href="{% url 'task-multi-setstatus' 'processed' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> processed</a> + <a href="{% url 'task-multi-setstatus' 'validated' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> validated</a> + <a href="{% url 'task-multi-setstatus' 'scrubbed' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> scrubbed</a> + <a href="{% url 'task-multi-setstatus' 'archived' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> archived</a> + <a href="{% url 'task-multi-setstatus' 'finished' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-sync-alt"></i> finished</a> </tr> </table> {% endif %} diff --git a/atdb/taskdatabase/templates/query/status_buttons_per_row.html b/atdb/taskdatabase/templates/query/status_buttons_per_row.html index 211b1026f717e3fb4f4e8edf23d874b72bafeef1..24eaac9fbcc98c9c8e4a4dfd20c6e9d09d4b2ffb 100644 --- a/atdb/taskdatabase/templates/query/status_buttons_per_row.html +++ b/atdb/taskdatabase/templates/query/status_buttons_per_row.html @@ -1,12 +1,7 @@ -{% if record.status == "defining" %} - <a href="{% url 'task-setstatus' record.pk 'defined' '1' %}" class="btn btn-secondary btn-sm" role="button"><i class="fas fa-sync-alt"></i> defined</a> - {% endif %} - - {% if record.status != "defining" %} - <a href="{% url 'task-setstatus' record.pk 'defining' '1' %}" class="btn btn-warning btn-sm" role="button"><i class="fas fa-undo"></i> defining</a> - {% endif %} - +{% if user.is_staff %} + <a href="{% url 'task-setstatus' record.pk 'defined' '1' %}" class="btn btn-secondary btn-sm" role="button"><i class="fas fa-sync-alt"></i> defined</a> + <a href="{% url 'task-setstatus' record.pk 'defining' '1' %}" class="btn btn-warning btn-sm" role="button"><i class="fas fa-undo"></i> defining</a> <a href="{% url 'task-setstatus' record.pk 'staged' '1' %}" class="btn btn-secondary btn-sm" role="button"><i class="fas fa-sync-alt"></i> staged</a> <a href="{% url 'task-setstatus' record.pk 'processed' '1' %}" class="btn btn-secondary btn-sm" role="button"><i class="fas fa-sync-alt"></i> processed</a> <a href="{% url 'task-setstatus' record.pk 'validated' '1' %}" class="btn btn-secondary btn-sm" role="button"><i class="fas fa-sync-alt"></i> validated</a> @@ -14,7 +9,4 @@ <a href="{% url 'task-setstatus' record.pk 'archived' '1' %}" class="btn btn-secondary btn-sm" role="button"><i class="fas fa-sync-alt"></i> archived</a> <a href="{% url 'task-setstatus' record.pk 'finished' '1' %}" class="btn btn-secondary btn-sm" role="button"><i class="fas fa-sync-alt"></i> finished</a> - {% if record.status == "processed" %} - <a href="{% url 'task-setstatus' record.pk 'validated' '1' %}" class="btn btn-success btn-sm" role="button">Validate</a> - {% endif %} - +{% endif %} diff --git a/atdb/taskdatabase/templates/taskdatabase/filter/filter_buttons.html b/atdb/taskdatabase/templates/taskdatabase/filter/filter_buttons.html index 96bd9f0c4dd86af232548766be7c87bb4af01376..eef028f769b1260ece1ed7b277682d15082a82b7 100644 --- a/atdb/taskdatabase/templates/taskdatabase/filter/filter_buttons.html +++ b/atdb/taskdatabase/templates/taskdatabase/filter/filter_buttons.html @@ -9,7 +9,8 @@ <a href="{% url 'task-set-filter' 'all' %}" class="btn btn-success btn-sm" role="button"><i class="fas fa-layer-group"></i> All Tasks</a> <a href="{% url 'task-set-active-filter' %}" class="btn btn-success btn-sm" role="button"><i class="fas fa-layer-group"></i> Active Tasks</a> <a href="{% url 'task-set-filter' 'failed' %}" class="btn btn-danger btn-sm" role="button"><i class="fas fa-layer-group"></i> Failed Tasks</a> - + <a href="{% url 'task-set-onhold-filter' True %}" class="btn btn-warning btn-sm" role="button"><i class="fas fa-layer-group"></i> On Hold</a> + <a href="{% url 'task-set-filter' 'defining' %}" class="btn btn-secondary btn-sm" role="button">defining</a> <a href="{% url 'task-set-filter' 'defined' %}" class="btn btn-primary btn-sm" role="button">defined</a> <a href="{% url 'task-set-filter' 'staging' %}" class="btn btn-secondary btn-sm" role="button">staging</a> diff --git a/atdb/taskdatabase/templates/taskdatabase/index.html b/atdb/taskdatabase/templates/taskdatabase/index.html index 9fc89e2f992bbc1f30b17d418e875719fc0232ff..3ec33ed14c1e2c63d6624683a4d6ac09baa48f43 100644 --- a/atdb/taskdatabase/templates/taskdatabase/index.html +++ b/atdb/taskdatabase/templates/taskdatabase/index.html @@ -25,10 +25,10 @@ ID <a href="{% url 'sort-tasks' 'id' %}" class="btn btn-light btn-sm" role="button"><i class="fas fa-sort-down"></i></a> </th> - + <th>Details</th> <th> <a href="{% url 'sort-tasks' '-workflow' %}" class="btn btn-light btn-sm" role="button"><i class="fas fa-sort-up"></i></a> - WF + Workflow <a href="{% url 'sort-tasks' 'workflow' %}" class="btn btn-light btn-sm" role="button"><i class="fas fa-sort-down"></i></a> </th> <th> @@ -63,7 +63,7 @@ Size <a href="{% url 'sort-tasks' 'size_to_process' %}" class="btn btn-light btn-sm" role="button"><i class="fas fa-sort-down"></i></a> </th> - <th>Info</th> + <th>Actions</th> </tr> </thead> @@ -80,7 +80,7 @@ {% include 'taskdatabase/pagination.html' %} </div> </div> - <p class="footer"> Version 1.0.0 (15 mar 2021 - 13:00) + <p class="footer"> Version 1.0.0 (16 mar 2021 - 13:00) </div> diff --git a/atdb/taskdatabase/templates/taskdatabase/tasks.html b/atdb/taskdatabase/templates/taskdatabase/tasks.html index cb9f66c73aad5b6effc72a2ca9912bbdae857b5e..5e8267852c074eba6ad33122cbedc0ec3ebd2c2e 100644 --- a/atdb/taskdatabase/templates/taskdatabase/tasks.html +++ b/atdb/taskdatabase/templates/taskdatabase/tasks.html @@ -7,6 +7,16 @@ <td> <a href="{{ task.get_absolute_url }}" target="_blank">{{ task.id }} </a> </td> + <td> + {% if user.is_authenticated %} + <a class="btn btn-primary btn-sm" + href="{% url 'task-details' task.id my_tasks.number %}" + data-popup-url="{% url 'task-details' task.id my_tasks.number %}" + ><i class="fas fa-list"></i> Details + </a> + + {% endif %} + </td> <td> <a class="open-modal btn btn-primary btn-sm" href="{% url 'workflow-details' task.workflow %}" @@ -37,17 +47,6 @@ <td>{{ task.creationTime|date:"Y-m-d H:i:s" }} </td> <td>{{ task.size_to_process|filesizeformat }} </td> - - <td> - {% if user.is_authenticated %} - <a class="btn btn-primary btn-sm" - href="{% url 'task-details' task.id my_tasks.number %}" - data-popup-url="{% url 'task-details' task.id my_tasks.number %}" - ><i class="fas fa-list"></i> Details - </a> - - {% endif %} - </td> <td> diff --git a/atdb/taskdatabase/urls.py b/atdb/taskdatabase/urls.py index cc58b8d3689d6a9598ca611b3ab4b4931dbac499..5192da51b18f8d1d197f7d02823f7504c73378b6 100644 --- a/atdb/taskdatabase/urls.py +++ b/atdb/taskdatabase/urls.py @@ -41,7 +41,6 @@ urlpatterns = [ path('configuration/', views.ConfigurationListViewAPI.as_view()), path('configuration/<int:pk>/', views.ConfigurationDetailsViewAPI.as_view(), name='configuration-detail-view-api'), - # --- custom requests --- # /atdb/get_size?status__in=defined,staged path('tasks/get_size', views.GetSizeView.as_view(), name='get-size-view'), @@ -57,7 +56,10 @@ urlpatterns = [ path('tasks/set_filter/<filter>', views.TaskSetFilter, name='task-set-filter'), path('tasks/set_active_filter', views.TaskSetActiveFilter, name='task-set-active-filter'), + path('tasks/task-set-onhold-filter/<onhold>', views.TaskSetOnHoldFilter, name='task-set-onhold-filter'), path('tasks/<int:pk>/set_status/<new_status>/<page>', views.TaskSetStatusTables2, name = 'task-setstatus'), + path('tasks/set_status_multi/<new_status>', views.TaskSetStatusMultiTables2, name='task-multi-setstatus'), + path('tasks/<int:pk>/hold/<hold_it>/<page>', views.Hold, name='task-hold-resume'), ] \ No newline at end of file diff --git a/atdb/taskdatabase/views.py b/atdb/taskdatabase/views.py index f2143ac3c0eb5b5395bb97d6b737128d129fbad1..93278874a39844cb29b8d130d4f54791cf5ba6f5 100644 --- a/atdb/taskdatabase/views.py +++ b/atdb/taskdatabase/views.py @@ -8,6 +8,7 @@ from django.contrib.auth.decorators import login_required from django.views.generic import ListView from django.contrib import messages +from django.core.cache import cache from rest_framework import generics, pagination from rest_framework.response import Response @@ -61,8 +62,8 @@ class TaskFilterQueryPage(filters.FilterSet): model = Task fields = { + 'id': ['exact', 'lte', 'gte'], 'workflow__id': ['exact'], - 'filter': ['exact', 'icontains'], 'priority': ['exact', 'lte', 'gte'], 'status': ['icontains', 'in'], 'project': ['exact', 'icontains'], @@ -110,6 +111,21 @@ class QueryView(SingleTableMixin, FilterView): template_name = "query/index.html" filterset_class = TaskFilterQueryPage + def get_table_data(self): + + # https://stackoverflow.com/questions/7763115/django-passing-data-between-views + + count = self.object_list.count() + + # get list of id's + LIMIT = settings.QUERY_LIMIT_MULTI_CHANGE + query_list_of_ids = list(self.object_list.values_list('id'))[:LIMIT] + + print(query_list_of_ids) + + # store on the session + self.request.session['query_list_of_ids'] = query_list_of_ids + return self.object_list # ---------- GUI Views ----------- @@ -143,6 +159,15 @@ class IndexView(ListView): except: pass + # check if there is a 'task_onhold_filter' put on the session + try: + onhold = self.request.session['task_onhold_filter'] + if onhold!=None: + tasks = tasks.filter(resume=not onhold) + + except: + pass + if (search_box is not None): tasks = get_searched_tasks(search_box, sort) @@ -187,7 +212,7 @@ def get_searched_tasks(search, sort): class TaskTables2View(SingleTableView): model = Task table_class = TaskTable - template_name = 'query/tables2.html' + template_name = 'query/query.html' def TaskDetails(request, id=0, page=0): @@ -400,6 +425,11 @@ def TaskSetStatus(request,pk,new_status,page=0): # set a filter value in the session, used later by the 'get_searched_tasks' mechanism def TaskSetFilter(request,filter): request.session['task_filter'] = filter + + # switch off the other filters + if filter == 'all': + request.session['task_onhold_filter'] = None + return redirect('/atdb/?page=1') # set the defined list of ACTIVE_STATUSSES on the session, used later by the 'get_searched_tasks' mechanism @@ -407,6 +437,10 @@ def TaskSetActiveFilter(request): request.session['task_filter'] = settings.ACTIVE_STATUSSES return redirect('/atdb/?page=1') +def TaskSetOnHoldFilter(request, onhold): + request.session['task_onhold_filter'] = onhold + return redirect('/atdb/?page=1') + @login_required def TaskChangePriority(request,pk,priority_change,page=0): @@ -433,6 +467,7 @@ def SortTasks(request,sort): request.session['sort'] = sort return redirect('/atdb') + @login_required def TaskSetStatusTables2(request,pk,new_status,page): model = Task @@ -441,6 +476,23 @@ def TaskSetStatusTables2(request,pk,new_status,page): task.save() return redirect('/atdb/query/?page='+page) +@login_required +def TaskSetStatusMultiTables2(request,new_status): + # read the current querylist from the session + + # yikes, this doesn't work if 2 users are simultaneously logged in + # query_list = cache.get('query_list') + # cache.delete('query_list') + + # get the list of id's from the session + query_list_of_ids = request.session['query_list_of_ids'] + print(query_list_of_ids) + + for id in query_list_of_ids: + task = Task.objects.get(id=id[0]) + task.new_status = new_status + task.save() + return redirect('/atdb/query') # /atdb/get_size?status__in=defined,staged class GetSizeView(generics.ListAPIView): @@ -453,7 +505,8 @@ class GetSizeView(generics.ListAPIView): try: status_in = query_params['status__in'] status_list=status_in[0].split(',') - print(status_list) + if status_list==['']: + status_list = settings.STATUSSES_WITH_DATA except: # if no 'status__in=' is given, then use the default list status_list = settings.STATUSSES_WITH_DATA