1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
# Copyright (C) 2013-2019 Linaro Limited
#
# Author: Neil Williams <neil.williams@linaro.org>
# Remi Duraffort <remi.duraffort@linaro.org>
#
# SPDX-License-Identifier: GPL-2.0-or-later
from __future__ import annotations
from datetime import timedelta
from typing import TYPE_CHECKING
import django_tables2 as tables
from django.conf import settings
from django.db.models import Q
from django.utils import timezone
if TYPE_CHECKING:
from typing import ClassVar, Iterator
from django.http import HttpRequest
class LavaView(tables.SingleTableView):
def __init__(self, request: HttpRequest, **kwargs):
super().__init__(**kwargs)
self.request = request
def get_table_data(self, prefix: str = ""):
"""Create queryset and add filters based on HTTP query of the request.
4 types of filters:
1). Simple text field search. Field must contain value.
2). Custom query filter. Query function will be called with passed value.
3). General search. Searches all simple text fields and all queries.
4). Time filter. Difference between now and field value cannot be larger
than value.
:return: filtered queryset
"""
data = self.get_queryset()
if not self.table_class or not hasattr(self.table_class, "Meta"):
return data
q = Q()
# Simple text field search
for field_name in self.table_class.Meta.searches.keys():
searched_value = self.request.GET.get(f"{prefix}{field_name}")
if field_name in self.table_class.base_columns:
field_name = (
self.table_class.base_columns[field_name].accessor or field_name
)
if searched_value:
q &= Q(**{f"{field_name}__contains": searched_value})
# Query
for func_attr_name, http_query_key in self.table_class.Meta.queries.items():
queried_value = self.request.GET.get(f"{prefix}{http_query_key}")
if queried_value:
q &= getattr(self, func_attr_name)(queried_value)
# general OR searches
general_search = self.request.GET.get(f"{prefix}search")
if general_search:
# Search by searchable fields
for field_name, field_operator in self.table_class.Meta.searches.items():
if field_name in self.table_class.base_columns:
field_name = (
self.table_class.base_columns[field_name].accessor or field_name
)
q |= Q(**{f"{field_name}__{field_operator}": general_search})
# Search inside queryable queries
for func_attr_name in self.table_class.Meta.queries.keys():
q |= getattr(self, func_attr_name)(general_search)
# Time filter
for field_name, time_unit in self.table_class.Meta.times.items():
time_search = self.request.GET.get(field_name)
if time_search:
q &= Q(
**{
f"{field_name}__gte": timezone.now()
- timedelta(**{time_unit: int(time_search)})
}
)
return data.filter(q)
class LavaTable(tables.Table):
"""
Base class for all django-tables2 support in LAVA
Provides search wrapper support for single tables
and tables using prefixes, as well as a default page length.
"""
def __init__(self, *args, request: HttpRequest = None, prefix: str = "", **kwargs):
super().__init__(*args, **kwargs)
self.request = request
if (
self.request
and self.request.user.is_authenticated
and hasattr(self.request.user, "extendeduser")
and self.request.user.extendeduser.table_length
):
self.length = self.request.user.extendeduser.table_length
else:
self.length = settings.DEFAULT_TABLE_LENGTH
self.empty_text = "No data available in table"
self.prefix = prefix
@classmethod
def has_searches(cls) -> bool:
return any((cls.Meta.searches, cls.Meta.queries, cls.Meta.times))
def get_simple_text_searches(self) -> Iterator[str]:
for field_name in self.Meta.searches:
yield self.prefix + field_name
def get_query_searches(self) -> Iterator[str]:
for field_name in self.Meta.queries.values():
yield self.prefix + field_name
def get_time_searches(self) -> Iterator[str]:
for field_name in self.Meta.times:
yield self.prefix + field_name
class Meta:
attrs: ClassVar[dict[str, str]] = {
"class": "table table-striped",
"width": "100%",
}
template_name: ClassVar[str] = "tables.html"
per_page_field: ClassVar[str] = "length"
# LAVA table searches
searches: ClassVar[dict[str, str]] = {}
queries: ClassVar[dict[str, str]] = {}
times: ClassVar[dict[str, str]] = {}
|