filter design and wip implementation
This commit is contained in:
		
							
								
								
									
										96
									
								
								filter-design.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								filter-design.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
			
		||||
# Django
 | 
			
		||||
 | 
			
		||||
Session.objects.filter(timestamp_start__year=2024)
 | 
			
		||||
 | 
			
		||||
# JSON
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "type": "session_start",
 | 
			
		||||
    "operator": "equals",
 | 
			
		||||
    "value": "2024"
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# HTML
 | 
			
		||||
```html
 | 
			
		||||
<select name="filters">
 | 
			
		||||
    <option value='[{"type": "session_start", "operator": "equals", "value": "2024"}]'>2024</option>
 | 
			
		||||
    <option value='[{"type": "session_start", "operator": "equals", "value": "2023"}]'>2023</option>
 | 
			
		||||
</select>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Python: Python -> HTML
 | 
			
		||||
```python
 | 
			
		||||
filters = [
 | 
			
		||||
    {
 | 
			
		||||
        "type": "session_start",
 | 
			
		||||
        "operator": "equals",
 | 
			
		||||
        "value": "2024"
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# predefined values
 | 
			
		||||
session_start_select = Select(name="filters", children=session_start_options)
 | 
			
		||||
session_start_options = [
 | 
			
		||||
    Option(value=create_filter("session_start", "equals", value=year))
 | 
			
		||||
    for year in range(2000, 2024)
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
# user-selected values
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Python: JSON -> Django
 | 
			
		||||
```python
 | 
			
		||||
filter_types = {
 | 
			
		||||
    "session_start": {
 | 
			
		||||
        "equals": "timestamp_start__exact=",
 | 
			
		||||
        "isnull": "timestamp_start__exact=None",
 | 
			
		||||
        "greater_than": "timestamp_start__gt=",
 | 
			
		||||
        "less_than": "timestamp_start__lt=",
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
# filter_string = request.GET.get("filters")
 | 
			
		||||
filter_string = """
 | 
			
		||||
{
 | 
			
		||||
    "type": "session_start",
 | 
			
		||||
    "operator": "equals",
 | 
			
		||||
    "value": "2024"
 | 
			
		||||
}
 | 
			
		||||
"""
 | 
			
		||||
def string_to_django_filter_dict(s: str):
 | 
			
		||||
    if s[-1] == "=":
 | 
			
		||||
        s + value
 | 
			
		||||
    key, value = s.split("=")
 | 
			
		||||
    return {key: value}
 | 
			
		||||
    
 | 
			
		||||
filter_obj = json.loads(filter_string)[0]
 | 
			
		||||
field, operator, value = filter_obj
 | 
			
		||||
 | 
			
		||||
if type in filter_types:
 | 
			
		||||
    if operator in filter_types[type]:
 | 
			
		||||
        queryset.filter(Q(**string_to_django_filter_dict(filter_types[type][operator])}))
 | 
			
		||||
    else:
 | 
			
		||||
        return False
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Python: Django -> JSON -> URI param
 | 
			
		||||
```python
 | 
			
		||||
filters = [
 | 
			
		||||
    {
 | 
			
		||||
        "type": "session_start",
 | 
			
		||||
        "operator": "equals",
 | 
			
		||||
        "value": "2024"
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
context = {
 | 
			
		||||
    "filters": json.dumps(filters)
 | 
			
		||||
}
 | 
			
		||||
return render("filter.html", context)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# Python: Django -> JSON (function)
 | 
			
		||||
```python
 | 
			
		||||
create_filter("session_start", "operator": "equals", "value": "2024")
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										61
									
								
								games/filters.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								games/filters.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
import json
 | 
			
		||||
from typing import TypeAlias, TypedDict, TypeVar
 | 
			
		||||
 | 
			
		||||
from django.db.models import Model, Q
 | 
			
		||||
from django.db.models.query import QuerySet
 | 
			
		||||
 | 
			
		||||
filter_types = {
 | 
			
		||||
    "session_start": {
 | 
			
		||||
        "equals": "timestamp_start__year__exact=",
 | 
			
		||||
        "isnull": "timestamp_start__exact=None",
 | 
			
		||||
        "greater_than": "timestamp_start__gt=",
 | 
			
		||||
        "less_than": "timestamp_start__lt=",
 | 
			
		||||
    },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Filter(TypedDict):
 | 
			
		||||
    name: str
 | 
			
		||||
    operator: str
 | 
			
		||||
    value: str
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
FilterList: TypeAlias = list[Filter]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def string_to_django_filter_dict(s: str, value: str = "") -> dict[str, str | int]:
 | 
			
		||||
    if s[-1] == "=":
 | 
			
		||||
        s += value
 | 
			
		||||
    key, value = s.split("=")
 | 
			
		||||
    return {key: value}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
T = TypeVar("T", bound=Model)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def apply_json_filter[T](s: str, queryset: QuerySet[T]) -> QuerySet[T]:
 | 
			
		||||
    filter_obj = urlsafe_json_decode(s)
 | 
			
		||||
    name, operator, value = (filter_obj[k] for k in ["name", "operator", "value"])
 | 
			
		||||
    if name in filter_types:
 | 
			
		||||
        if operator in filter_types[name]:
 | 
			
		||||
            filtered = queryset.filter(
 | 
			
		||||
                Q(**string_to_django_filter_dict(filter_types[name][operator], value))
 | 
			
		||||
            )
 | 
			
		||||
            return filtered
 | 
			
		||||
    return queryset
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
urlsafe_encode_table = str.maketrans({",": "^", ":": "-", " ": ""})
 | 
			
		||||
urlsafe_decode_table = str.maketrans({"^": ",", "-": ":"})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def urlsafe_json_encode[T](obj: T) -> str:
 | 
			
		||||
    json_string = json.dumps(obj)
 | 
			
		||||
    safe_string = json_string.translate(urlsafe_encode_table)
 | 
			
		||||
    return safe_string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def urlsafe_json_decode[T](s: str) -> T:
 | 
			
		||||
    unsafe_string = s.translate(urlsafe_decode_table)
 | 
			
		||||
    obj = json.loads(unsafe_string)
 | 
			
		||||
    return obj
 | 
			
		||||
		Reference in New Issue
	
	Block a user