Skip to content

Commit

Permalink
Merge pull request #384 from msqd/filter-tpdex
Browse files Browse the repository at this point in the history
Filter tpdex
  • Loading branch information
ArthurD1 authored Jun 14, 2024
2 parents 681814d + 2daa6f4 commit eb3b994
Show file tree
Hide file tree
Showing 27 changed files with 870 additions and 62 deletions.
1 change: 1 addition & 0 deletions docs/reference/apps/harp_apps.dashboard.filters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ Submodules
harp_apps.dashboard.filters.transaction_flag
harp_apps.dashboard.filters.transaction_method
harp_apps.dashboard.filters.transaction_status
harp_apps.dashboard.filters.transaction_tpdex
harp_apps.dashboard.filters.utils
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
harp_apps.dashboard.filters.transaction_tpdex
=============================================

.. automodule:: harp_apps.dashboard.filters.transaction_tpdex
:members:
:undoc-members:
:show-inheritance:
17 changes: 4 additions & 13 deletions harp_apps/dashboard/controllers/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
TransactionFlagFacet,
TransactionMethodFacet,
TransactionStatusFacet,
flatten_facet_value,
TransactionTpdexFacet,
)

logger = get_logger(__name__)
Expand All @@ -31,6 +31,7 @@ def __init__(self, *, storage: Storage, handle_errors=True, router=None):
TransactionMethodFacet(),
TransactionStatusFacet(),
TransactionFlagFacet(),
TransactionTpdexFacet(),
)
}

Expand All @@ -41,12 +42,7 @@ async def filters(self, request: HttpRequest):
await self.facets["endpoint"].refresh()

return json(
{
name: facet.filter(
flatten_facet_value(request.query.getall(name, [])),
)
for name, facet in self.facets.items()
},
{name: facet.filter_from_query(request.query) for name, facet in self.facets.items()},
)

@GetHandler("/")
Expand All @@ -59,12 +55,7 @@ async def list(self, request: HttpRequest):

results = await self.storage.get_transaction_list(
with_messages=True,
filters={
name: facet.get_filter(
flatten_facet_value(request.query.getall(name, [])),
)
for name, facet in self.facets.items()
},
filters={name: facet.get_filter_from_query(request.query) for name, facet in self.facets.items()},
page=page,
cursor=cursor,
username=request.context.get("user") or "anonymous",
Expand Down
2 changes: 2 additions & 0 deletions harp_apps/dashboard/filters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
from .transaction_flag import TransactionFlagFacet
from .transaction_method import TransactionMethodFacet
from .transaction_status import TransactionStatusFacet
from .transaction_tpdex import TransactionTpdexFacet
from .utils import flatten_facet_value

__all__ = [
"TransactionMethodFacet",
"TransactionStatusFacet",
"TransactionEndpointFacet",
"TransactionFlagFacet",
"TransactionTpdexFacet",
"flatten_facet_value",
]
82 changes: 77 additions & 5 deletions harp_apps/dashboard/filters/base.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,38 @@
from typing import Optional

from multidict import MultiDictProxy

from harp.typing.storage import Storage
from harp_apps.dashboard.filters.utils import flatten_facet_value, str_to_float_or_none


class AbstractFacet:
name = None
choices = set()
exhaustive = True
name = "name"

def __init__(self):
self.meta = {}

@property
def values(self):
raise NotImplementedError

def get_filter(self, raw_data: list):
raise NotImplementedError

def filter(self, raw_data: list):
raise NotImplementedError

def get_filter_from_query(self, query: MultiDictProxy):
raise NotImplementedError

def filter_from_query(self, query: MultiDictProxy):
raise NotImplementedError


class AbstractChoicesFacet(AbstractFacet):
choices = set()
exhaustive = True

@property
def values(self):
return [{"name": choice, "count": self.meta.get(choice, {}).get("count", None)} for choice in self.choices]
Expand All @@ -17,20 +41,68 @@ def get_filter(self, raw_data: list):
query_endpoints = self.choices.intersection(raw_data)
return list(query_endpoints) if len(query_endpoints) else None

def get_filter_from_query(self, query: MultiDictProxy):
raw_data = self._choices_from_query(query)
return self.get_filter(raw_data)

def filter(self, raw_data: list):
return {
"values": self.values,
"current": self.get_filter(raw_data),
}

def filter_from_query(self, query: MultiDictProxy):
raw_data = self._choices_from_query(query)
return self.filter(raw_data)

def _choices_from_query(self, query: MultiDictProxy):
return flatten_facet_value(query.getall(self.name, []))

class FacetWithStorage(AbstractFacet):

class FacetWithStorage(AbstractChoicesFacet):
def __init__(self, *, storage: Storage):
super().__init__()
self.storage = storage


class NonExhaustiveFacet(AbstractFacet):
class AbstractMinMaxFacet(AbstractFacet):
min: float = 0.0
max: float = 100.0

def __init__(self):
self.meta = {}

@property
def values(self):
return {
"min": self.meta.get("min", None),
"max": self.meta.get("max", None),
}

def filter(self, min, max):
return {
"values": ["min", "max"],
"current": self.get_filter(min, max),
}

def filter_from_query(self, query: MultiDictProxy):
min, max = self._min_max_from_query(query)
return self.filter(min, max)

def get_filter(self, min: Optional[float], max: Optional[float]):
return {"min": min, "max": max}

def get_filter_from_query(self, query: MultiDictProxy):
min, max = self._min_max_from_query(query)
return self.get_filter(min, max)

def _min_max_from_query(self, query: MultiDictProxy):
return str_to_float_or_none(query.get(self.name + "min", "")), str_to_float_or_none(
query.get(self.name + "max", "")
)


class NonExhaustiveFacet(AbstractChoicesFacet):
exhaustive = False

def __init__(self):
Expand Down
6 changes: 0 additions & 6 deletions harp_apps/dashboard/filters/tests/test_base.py

This file was deleted.

4 changes: 2 additions & 2 deletions harp_apps/dashboard/filters/transaction_method.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .base import AbstractFacet
from .base import AbstractChoicesFacet


class TransactionMethodFacet(AbstractFacet):
class TransactionMethodFacet(AbstractChoicesFacet):
name = "method"
choices = {"GET", "POST", "PUT", "DELETE", "PATCH"}
4 changes: 2 additions & 2 deletions harp_apps/dashboard/filters/transaction_status.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .base import AbstractFacet
from .base import AbstractChoicesFacet


class TransactionStatusFacet(AbstractFacet):
class TransactionStatusFacet(AbstractChoicesFacet):
name = "status"
choices = {"2xx", "3xx", "4xx", "5xx"}
5 changes: 5 additions & 0 deletions harp_apps/dashboard/filters/transaction_tpdex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .base import AbstractMinMaxFacet


class TransactionTpdexFacet(AbstractMinMaxFacet):
name = "tpdex"
8 changes: 8 additions & 0 deletions harp_apps/dashboard/filters/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from itertools import chain
from typing import Optional


def flatten_facet_value(values: list):
Expand All @@ -7,3 +8,10 @@ def flatten_facet_value(values: list):
*map(lambda x: x.split(","), values),
),
)


def str_to_float_or_none(s: str) -> Optional[float]:
try:
return float(s)
except ValueError:
return None
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,28 @@ import { useQuery } from "react-query"
import { useApi } from "Domain/Api"
import { ItemList } from "Domain/Api/Types"
import { Transaction } from "Models/Transaction"
import { Filter, Filters } from "Types/filters"
import { FilterValue, Filters, MinMaxFilter } from "Types/filters"

function getQueryStringFromRecord(
filters: Record<string, Filter> | { page: number; cursor?: string | null; search?: string | null },
filters: Record<string, FilterValue> | { page: number; cursor?: string | null; search?: string | null },
) {
const searchParams = new URLSearchParams()

for (const [key, value] of Object.entries(filters) as [string, string | number | undefined][]) {
for (const [key, value] of Object.entries(filters)) {
if (value) {
searchParams.append(key, value.toString())
if (Array.isArray(value)) {
searchParams.append(key, value.toString())
} else if (typeof value === "object") {
const minMaxFilter = value as MinMaxFilter
if (minMaxFilter.min !== undefined) {
searchParams.set(`${key}min`, minMaxFilter.min.toString())
}
if (minMaxFilter.max !== undefined) {
searchParams.set(`${key}max`, minMaxFilter.max.toString())
}
} else {
searchParams.set(key, value.toString())
}
}
}
return searchParams.toString()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline"
import { ChangeEvent, useState } from "react"

import { Filter } from "Types/filters"
import { ArrayFilter } from "Types/filters"
import { Checkbox, Radio } from "ui/Components/FormWidgets"
import { H5 } from "ui/Components/Typography"

Expand All @@ -14,8 +14,8 @@ interface FacetProps {
type: "checkboxes" | "radios"
defaultOpen?: boolean
meta: Array<{ name: string; count?: number }>
values?: Filter
setValues?: (value: Filter) => unknown
values?: ArrayFilter
setValues?: (value: ArrayFilter) => unknown
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline"
import { useState } from "react"

import { MinMaxFilter } from "Types/filters"
import { RangeSlider, Mark } from "ui/Components/Slider/RangeSlider.tsx"
import { H5 } from "ui/Components/Typography"

import { FacetInnerLightButton } from "./FacetInnerLightButton.tsx"

interface RangeSliderFacetProps {
title: string
name: string
type: "rangeSlider"
defaultOpen?: boolean
values?: MinMaxFilter
setValues: (value?: MinMaxFilter) => void
marks?: Mark[]
min?: number
max?: number
}

export function RangeSliderFacet({
title,
name,
values = undefined,
setValues,
defaultOpen = true,
marks,
min,
max,
}: RangeSliderFacetProps) {
const [open, setOpen] = useState(defaultOpen)

return (
<div className="px-4 py-3">
<fieldset name={name}>
<H5 as="legend" padding="pt-0" className="flex w-full cursor-pointer" onClick={() => setOpen(!open)}>
<span className="grow">
{title}
{values ? <FacetInnerLightButton label="reset" handler={() => setValues(undefined)} /> : null}
</span>
{open ? (
<ChevronUpIcon className="h-4 w-4 text-gray-600" />
) : (
<ChevronDownIcon className="h-4 w-4 text-gray-600" />
)}
</H5>
<div className={"mt-2 space-y-2 " + (open ? "" : "hidden")}>
<RangeSlider
min={min}
max={max}
step={10}
defaultValue={values}
onPointerUp={setValues}
marks={marks}
thumbSize="8px"
/>
</div>
</fieldset>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { Facet } from "./Facet"
export { FacetLabel } from "./FacetLabel"
export { RangeSliderFacet } from "./RangeSliderFacet"
Loading

0 comments on commit eb3b994

Please sign in to comment.