Loading src/jama/templates/task_result_details.html 0 → 100644 +45 −0 Original line number Diff line number Diff line {% extends 'common.html' %} {% block title %} Task Result Details {% endblock %} {% block content %} <style> body { max-width: unset; width: 100%; } .task-result-details-wrapper { width: 100%; } .task-result-details-wrapper table { width: 100%; table-layout: fixed; } .task-result-details-wrapper th, .task-result-details-wrapper td { vertical-align: top; } </style> <div class="task-result-details-wrapper"> <h1>DB Task Result Details</h1> <p> <a href="{% url 'task_results' %}">← Retour à la liste</a> </p> <table> <thead> <tr> <th>champ</th> <th>valeur</th> </tr> </thead> <tbody> {% for row in detail_rows %} <tr> <td><strong>{{ row.name }}</strong></td> <td><pre style="margin:0;white-space:pre-wrap;">{{ row.value }}</pre></td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %} src/jama/templates/task_results.html 0 → 100644 +113 −0 Original line number Diff line number Diff line {% extends 'common.html' %} {% block title %} Task Results {% endblock %} {% block content %} <style> body { max-width: unset; width: 100%; } .task-results-wrapper { width: 100%; } .task-results-wrapper table { width: 100%; table-layout: fixed; } .task-results-wrapper th, .task-results-wrapper td { vertical-align: top; } </style> <div class="task-results-wrapper"> <h1>DB Task Results</h1> <p> {{ page_obj.paginator.count }} résultat(s) au total. Affichage page {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}. </p> <form method="get"> <input type="hidden" name="sort" value="{{ sort }}"> <p> <button type="submit">Filtrer</button> <a href="{{ request.path }}">Réinitialiser</a> </p> <div style="overflow-x: auto;"> <table> <thead> <tr> {% for column in columns %} <th> {% if column.sortable %} <a href="?{{ column.sort_link }}"> {{ column.label }} {% if column.is_sorted %} {% if sort_desc %}▼{% else %}▲{% endif %} {% endif %} </a> {% else %} {{ column.label }} {% endif %} </th> {% endfor %} </tr> <tr> {% for column in columns %} <th> {% if column.filterable %} {% if column.name == "status" %} <select name="f_status"> <option value="">Tous statuts</option> {% for status_value in status_filter_values %} <option value="{{ status_value }}" {% if column.filter_value == status_value %}selected{% endif %}> {{ status_value }} </option> {% endfor %} </select> {% else %} <input type="text" name="f_{{ column.name }}" value="{{ column.filter_value }}" placeholder="Filtrer {{ column.name }}" /> {% endif %} {% endif %} </th> {% endfor %} </tr> </thead> <tbody> {% for row in rows %} <tr> {% for value in row %} <td> {% if forloop.first %} <a href="{{ value }}"><button type="button">details</button></a> {% else %} <pre style="margin:0;white-space:pre-wrap;">{{ value }}</pre> {% endif %} </td> {% endfor %} </tr> {% empty %} <tr> <td colspan="{{ columns|length }}">Aucun résultat.</td> </tr> {% endfor %} </tbody> </table> </div> </form> <p> {% if previous_page_link %} <a href="?{{ previous_page_link }}">← Page précédente</a> {% endif %} {% if next_page_link %} <a href="?{{ next_page_link }}" style="margin-left: 1em;">Page suivante →</a> {% endif %} </p> </div> {% endblock %} src/jama/urls.py +10 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,16 @@ urlpatterns = [ path(settings.JAMA_URL_BASE_PATH + "rpc/", include("rpc.urls")), path(settings.JAMA_URL_BASE_PATH + "resources/", include("resources.urls")), path(settings.JAMA_URL_BASE_PATH + "status", views.status), path( settings.JAMA_URL_BASE_PATH + "task-results/", views.task_results, name="task_results", ), path( settings.JAMA_URL_BASE_PATH + "task-results/<uuid:task_result_id>/", views.task_result_details, name="task_result_details", ), path(settings.JAMA_URL_BASE_PATH + "iiif/", include("iiif.urls", namespace="iiif")), path(settings.JAMA_URL_BASE_PATH + "hls/", include("hls.urls", namespace="hls")), ] Loading src/jama/views.py +227 −92 Original line number Diff line number Diff line Loading @@ -2,6 +2,12 @@ from django.http import HttpResponse, HttpRequest from django.shortcuts import render, get_object_or_404, redirect from jama import settings from django.contrib.auth.models import User from django.contrib.auth.decorators import user_passes_test from django.core.paginator import Paginator from django.db.models import F, TextField from django.db.models.functions import Cast from django.urls import reverse from django_tasks_db.models import DBTaskResult from resources.models import Resource, Collection from urllib.parse import urlparse, parse_qs, urlencode, urlunparse Loading Loading @@ -90,3 +96,132 @@ def ark_collection(request: HttpRequest, collection_id: int) -> HttpResponse: location = merge_query_string_to_url(location, request.META["QUERY_STRING"]) return redirect(location) return HttpResponse("Jama Collection ARK location placeholder") @user_passes_test(lambda user: user.is_superuser) def task_results(request: HttpRequest) -> HttpResponse: fields = list(DBTaskResult._meta.fields) field_names = [field.name for field in fields] status_filter_values = ["SUCCESSFUL", "FAILED", "RUNNING", "READY"] displayed_field_names = ["task_path", "args_kwargs", "enqueued_at", "status"] displayed_field_labels = { "task_path": "task_path", "args_kwargs": "args/kwargs", "enqueued_at": "enqueued_at", "status": "status", } filter_values = { field_name: request.GET.get(f"f_{field_name}", "").strip() for field_name in field_names } task_results_queryset = DBTaskResult.objects.all() for idx, (field_name, filter_value) in enumerate(filter_values.items()): if not filter_value: continue if field_name == "status": if filter_value not in status_filter_values: continue task_results_queryset = task_results_queryset.filter(status=filter_value) continue # cast any field type to text so filtering can be done uniformly alias = f"_as_text_{idx}" task_results_queryset = task_results_queryset.annotate( **{alias: Cast(F(field_name), output_field=TextField())} ).filter(**{f"{alias}__icontains": filter_value}) sort = request.GET.get("sort", "-enqueued_at") sort_field = sort[1:] if sort.startswith("-") else sort if sort_field not in field_names: sort = "-enqueued_at" if "enqueued_at" in field_names else field_names[0] sort_field = sort[1:] if sort.startswith("-") else sort task_results_queryset = task_results_queryset.order_by(sort) sort_desc = sort.startswith("-") def build_query_string(**updates) -> str: query = request.GET.copy() for key, value in updates.items(): if value is None: query.pop(key, None) else: query[key] = str(value) return query.urlencode() columns = [ { "name": "details", "label": "details", "sort_link": None, "filter_value": "", "is_sorted": False, "sortable": False, "filterable": False, } ] for field_name in displayed_field_names: next_sort = ( f"-{field_name}" if sort_field == field_name and not sort_desc else field_name ) columns.append( { "name": field_name, "label": displayed_field_labels[field_name], "sort_link": build_query_string(sort=next_sort, page=1), "filter_value": filter_values[field_name], "is_sorted": sort_field == field_name, "sortable": True, "filterable": True, } ) paginator = Paginator(task_results_queryset, 100) page_obj = paginator.get_page(request.GET.get("page")) rows = [] for item in page_obj.object_list: row = [ reverse("task_result_details", kwargs={"task_result_id": item.id}), ] row.extend( [getattr(item, field_name, None) for field_name in displayed_field_names] ) rows.append(row) previous_page_link = None next_page_link = None if page_obj.has_previous(): previous_page_link = build_query_string(page=page_obj.previous_page_number()) if page_obj.has_next(): next_page_link = build_query_string(page=page_obj.next_page_number()) return render( request, "task_results.html", { "columns": columns, "rows": rows, "sort": sort, "sort_desc": sort_desc, "status_filter_values": status_filter_values, "page_obj": page_obj, "previous_page_link": previous_page_link, "next_page_link": next_page_link, }, ) @user_passes_test(lambda user: user.is_superuser) def task_result_details(request: HttpRequest, task_result_id) -> HttpResponse: task_result = get_object_or_404(DBTaskResult, pk=task_result_id) detail_rows = [] for field in DBTaskResult._meta.fields: detail_rows.append( {"name": field.name, "value": getattr(task_result, field.name, None)} ) return render( request, "task_result_details.html", {"task_result": task_result, "detail_rows": detail_rows}, ) Loading
src/jama/templates/task_result_details.html 0 → 100644 +45 −0 Original line number Diff line number Diff line {% extends 'common.html' %} {% block title %} Task Result Details {% endblock %} {% block content %} <style> body { max-width: unset; width: 100%; } .task-result-details-wrapper { width: 100%; } .task-result-details-wrapper table { width: 100%; table-layout: fixed; } .task-result-details-wrapper th, .task-result-details-wrapper td { vertical-align: top; } </style> <div class="task-result-details-wrapper"> <h1>DB Task Result Details</h1> <p> <a href="{% url 'task_results' %}">← Retour à la liste</a> </p> <table> <thead> <tr> <th>champ</th> <th>valeur</th> </tr> </thead> <tbody> {% for row in detail_rows %} <tr> <td><strong>{{ row.name }}</strong></td> <td><pre style="margin:0;white-space:pre-wrap;">{{ row.value }}</pre></td> </tr> {% endfor %} </tbody> </table> </div> {% endblock %}
src/jama/templates/task_results.html 0 → 100644 +113 −0 Original line number Diff line number Diff line {% extends 'common.html' %} {% block title %} Task Results {% endblock %} {% block content %} <style> body { max-width: unset; width: 100%; } .task-results-wrapper { width: 100%; } .task-results-wrapper table { width: 100%; table-layout: fixed; } .task-results-wrapper th, .task-results-wrapper td { vertical-align: top; } </style> <div class="task-results-wrapper"> <h1>DB Task Results</h1> <p> {{ page_obj.paginator.count }} résultat(s) au total. Affichage page {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}. </p> <form method="get"> <input type="hidden" name="sort" value="{{ sort }}"> <p> <button type="submit">Filtrer</button> <a href="{{ request.path }}">Réinitialiser</a> </p> <div style="overflow-x: auto;"> <table> <thead> <tr> {% for column in columns %} <th> {% if column.sortable %} <a href="?{{ column.sort_link }}"> {{ column.label }} {% if column.is_sorted %} {% if sort_desc %}▼{% else %}▲{% endif %} {% endif %} </a> {% else %} {{ column.label }} {% endif %} </th> {% endfor %} </tr> <tr> {% for column in columns %} <th> {% if column.filterable %} {% if column.name == "status" %} <select name="f_status"> <option value="">Tous statuts</option> {% for status_value in status_filter_values %} <option value="{{ status_value }}" {% if column.filter_value == status_value %}selected{% endif %}> {{ status_value }} </option> {% endfor %} </select> {% else %} <input type="text" name="f_{{ column.name }}" value="{{ column.filter_value }}" placeholder="Filtrer {{ column.name }}" /> {% endif %} {% endif %} </th> {% endfor %} </tr> </thead> <tbody> {% for row in rows %} <tr> {% for value in row %} <td> {% if forloop.first %} <a href="{{ value }}"><button type="button">details</button></a> {% else %} <pre style="margin:0;white-space:pre-wrap;">{{ value }}</pre> {% endif %} </td> {% endfor %} </tr> {% empty %} <tr> <td colspan="{{ columns|length }}">Aucun résultat.</td> </tr> {% endfor %} </tbody> </table> </div> </form> <p> {% if previous_page_link %} <a href="?{{ previous_page_link }}">← Page précédente</a> {% endif %} {% if next_page_link %} <a href="?{{ next_page_link }}" style="margin-left: 1em;">Page suivante →</a> {% endif %} </p> </div> {% endblock %}
src/jama/urls.py +10 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,16 @@ urlpatterns = [ path(settings.JAMA_URL_BASE_PATH + "rpc/", include("rpc.urls")), path(settings.JAMA_URL_BASE_PATH + "resources/", include("resources.urls")), path(settings.JAMA_URL_BASE_PATH + "status", views.status), path( settings.JAMA_URL_BASE_PATH + "task-results/", views.task_results, name="task_results", ), path( settings.JAMA_URL_BASE_PATH + "task-results/<uuid:task_result_id>/", views.task_result_details, name="task_result_details", ), path(settings.JAMA_URL_BASE_PATH + "iiif/", include("iiif.urls", namespace="iiif")), path(settings.JAMA_URL_BASE_PATH + "hls/", include("hls.urls", namespace="hls")), ] Loading
src/jama/views.py +227 −92 Original line number Diff line number Diff line Loading @@ -2,6 +2,12 @@ from django.http import HttpResponse, HttpRequest from django.shortcuts import render, get_object_or_404, redirect from jama import settings from django.contrib.auth.models import User from django.contrib.auth.decorators import user_passes_test from django.core.paginator import Paginator from django.db.models import F, TextField from django.db.models.functions import Cast from django.urls import reverse from django_tasks_db.models import DBTaskResult from resources.models import Resource, Collection from urllib.parse import urlparse, parse_qs, urlencode, urlunparse Loading Loading @@ -90,3 +96,132 @@ def ark_collection(request: HttpRequest, collection_id: int) -> HttpResponse: location = merge_query_string_to_url(location, request.META["QUERY_STRING"]) return redirect(location) return HttpResponse("Jama Collection ARK location placeholder") @user_passes_test(lambda user: user.is_superuser) def task_results(request: HttpRequest) -> HttpResponse: fields = list(DBTaskResult._meta.fields) field_names = [field.name for field in fields] status_filter_values = ["SUCCESSFUL", "FAILED", "RUNNING", "READY"] displayed_field_names = ["task_path", "args_kwargs", "enqueued_at", "status"] displayed_field_labels = { "task_path": "task_path", "args_kwargs": "args/kwargs", "enqueued_at": "enqueued_at", "status": "status", } filter_values = { field_name: request.GET.get(f"f_{field_name}", "").strip() for field_name in field_names } task_results_queryset = DBTaskResult.objects.all() for idx, (field_name, filter_value) in enumerate(filter_values.items()): if not filter_value: continue if field_name == "status": if filter_value not in status_filter_values: continue task_results_queryset = task_results_queryset.filter(status=filter_value) continue # cast any field type to text so filtering can be done uniformly alias = f"_as_text_{idx}" task_results_queryset = task_results_queryset.annotate( **{alias: Cast(F(field_name), output_field=TextField())} ).filter(**{f"{alias}__icontains": filter_value}) sort = request.GET.get("sort", "-enqueued_at") sort_field = sort[1:] if sort.startswith("-") else sort if sort_field not in field_names: sort = "-enqueued_at" if "enqueued_at" in field_names else field_names[0] sort_field = sort[1:] if sort.startswith("-") else sort task_results_queryset = task_results_queryset.order_by(sort) sort_desc = sort.startswith("-") def build_query_string(**updates) -> str: query = request.GET.copy() for key, value in updates.items(): if value is None: query.pop(key, None) else: query[key] = str(value) return query.urlencode() columns = [ { "name": "details", "label": "details", "sort_link": None, "filter_value": "", "is_sorted": False, "sortable": False, "filterable": False, } ] for field_name in displayed_field_names: next_sort = ( f"-{field_name}" if sort_field == field_name and not sort_desc else field_name ) columns.append( { "name": field_name, "label": displayed_field_labels[field_name], "sort_link": build_query_string(sort=next_sort, page=1), "filter_value": filter_values[field_name], "is_sorted": sort_field == field_name, "sortable": True, "filterable": True, } ) paginator = Paginator(task_results_queryset, 100) page_obj = paginator.get_page(request.GET.get("page")) rows = [] for item in page_obj.object_list: row = [ reverse("task_result_details", kwargs={"task_result_id": item.id}), ] row.extend( [getattr(item, field_name, None) for field_name in displayed_field_names] ) rows.append(row) previous_page_link = None next_page_link = None if page_obj.has_previous(): previous_page_link = build_query_string(page=page_obj.previous_page_number()) if page_obj.has_next(): next_page_link = build_query_string(page=page_obj.next_page_number()) return render( request, "task_results.html", { "columns": columns, "rows": rows, "sort": sort, "sort_desc": sort_desc, "status_filter_values": status_filter_values, "page_obj": page_obj, "previous_page_link": previous_page_link, "next_page_link": next_page_link, }, ) @user_passes_test(lambda user: user.is_superuser) def task_result_details(request: HttpRequest, task_result_id) -> HttpResponse: task_result = get_object_or_404(DBTaskResult, pk=task_result_id) detail_rows = [] for field in DBTaskResult._meta.fields: detail_rows.append( {"name": field.name, "value": getattr(task_result, field.name, None)} ) return render( request, "task_result_details.html", {"task_result": task_result, "detail_rows": detail_rows}, )