Filtres i ordres als resultats de cerca
This commit is contained in:
@@ -5,6 +5,7 @@ from fastapi import Request
|
||||
from fastapi.params import Param
|
||||
from folkugat_web.api.router import get_router
|
||||
from folkugat_web.fragments import temes
|
||||
from folkugat_web.model.search import Order, OrderBy, OrderParams
|
||||
from folkugat_web.services import auth
|
||||
from folkugat_web.templates import templates
|
||||
|
||||
@@ -17,9 +18,17 @@ def page(
|
||||
logged_in: auth.LoggedIn,
|
||||
query: Annotated[str, Param()] = "",
|
||||
properties: Annotated[list[str] | None, Param()] = None,
|
||||
order_by: OrderBy | None = None,
|
||||
order: Order = Order.DESC,
|
||||
):
|
||||
properties = properties or []
|
||||
content_url = f"/api/content/temes?{temes.build_temes_params(query=query, properties=properties)}"
|
||||
order_params = OrderParams(order_by=order_by, order=order) if order_by else None
|
||||
temes_params = temes.build_temes_params(
|
||||
query=query,
|
||||
properties=properties,
|
||||
order_params=order_params,
|
||||
)
|
||||
content_url = f"/api/content/temes?{temes_params}"
|
||||
return templates.TemplateResponse(
|
||||
"index.html",
|
||||
{
|
||||
@@ -38,13 +47,19 @@ def content(
|
||||
logged_in: auth.LoggedIn,
|
||||
query: Annotated[str, Param()] = "",
|
||||
properties: Annotated[list[str] | None, Param()] = None,
|
||||
order_by: OrderBy | None = None,
|
||||
order: Order = Order.DESC,
|
||||
):
|
||||
properties = properties or []
|
||||
if not query and not order_by:
|
||||
order_by = OrderBy.TIMES_PLAYED
|
||||
order_params = OrderParams(order_by=order_by, order=order) if order_by else None
|
||||
return temes.temes_pagina(
|
||||
request=request,
|
||||
logged_in=logged_in,
|
||||
query=query,
|
||||
properties=properties,
|
||||
order_params=order_params,
|
||||
)
|
||||
|
||||
|
||||
@@ -54,13 +69,17 @@ def busca(
|
||||
logged_in: auth.LoggedIn,
|
||||
query: Annotated[str, Param()],
|
||||
properties: Annotated[list[str] | None, Param()] = None,
|
||||
order_by: OrderBy | None = None,
|
||||
order: Order = Order.DESC,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
):
|
||||
order_params = OrderParams(order_by=order_by, order=order) if order_by else None
|
||||
return temes.temes_busca(
|
||||
request=request,
|
||||
query=query,
|
||||
properties=properties or [],
|
||||
order_params=order_params,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
logged_in=logged_in,
|
||||
|
||||
@@ -649,11 +649,6 @@ video {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.my-4 {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.mb-3 {
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
@@ -666,6 +661,10 @@ video {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.mr-1 {
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.mr-2 {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
@@ -718,6 +717,10 @@ video {
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.w-0 {
|
||||
width: 0px;
|
||||
}
|
||||
|
||||
.w-1\/2 {
|
||||
width: 50%;
|
||||
}
|
||||
@@ -988,10 +991,18 @@ video {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.pb-2 {
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.pl-5 {
|
||||
padding-left: 1.25rem;
|
||||
}
|
||||
|
||||
.pr-2 {
|
||||
padding-right: 0.5rem;
|
||||
}
|
||||
|
||||
.pt-4 {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
@@ -1056,9 +1067,9 @@ video {
|
||||
color: rgb(76 16 30 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-gray-500 {
|
||||
.text-gray-400 {
|
||||
--tw-text-opacity: 1;
|
||||
color: rgb(107 114 128 / var(--tw-text-opacity, 1));
|
||||
color: rgb(156 163 175 / var(--tw-text-opacity, 1));
|
||||
}
|
||||
|
||||
.text-red-400 {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% include "fragments/menu.html" %}
|
||||
<div class="p-12 text-center flex flex-col items-center justify-center">
|
||||
<h3 class="text-3xl text-beige p-4">Temes</h3>
|
||||
{% set hx_vars = build_hx_vars({"properties": properties_str}, order_params_dict) %}
|
||||
<input id="query-input"
|
||||
type="text" name="query" value="{{ query }}"
|
||||
placeholder="Busca una tema..."
|
||||
@@ -11,7 +12,7 @@
|
||||
hx-get="/api/temes/busca"
|
||||
hx-trigger="revealed, keyup delay:500ms changed"
|
||||
hx-target="#search-results"
|
||||
hx-vars="properties:{{ properties_str }}"
|
||||
hx-vars="{{ hx_vars }}"
|
||||
hx-swap="outerHTML">
|
||||
<div id="search-results" class="flex-col"></div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,33 @@
|
||||
{{ tema.title }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="px-4 text-sm text-gray-500">
|
||||
{% if tema.properties %}
|
||||
<ul class="flex flex-wrap text-sm px-3">
|
||||
{% for property in tema.properties %}
|
||||
{% set hx_vars = build_hx_vars({"properties": add_property_str(property.value)}, order_params_dict) %}
|
||||
<button class="bg-beige text-white rounded
|
||||
m-1 px-2"
|
||||
hx-get="/api/content/temes"
|
||||
hx-target="#content"
|
||||
hx-include="[name=query]"
|
||||
hx-vars="{{ hx_vars }}"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
{{ property.value }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if tema.main_score() and tema.main_score().preview_url %}
|
||||
<a href="/tema/{{ tema.id }}">
|
||||
<img class="px-4 pb-2"
|
||||
src="{{ tema.main_score().preview_url }}" />
|
||||
</a>
|
||||
{% endif %}
|
||||
<div class="flex flex-row w-full">
|
||||
<div class="flex flex-row items-center
|
||||
row-0 px-4 text-sm text-gray-400">
|
||||
<i class="mx-1">{% include "icons/music-box.svg" %}</i>
|
||||
{% if tema.stats %}
|
||||
Tocat {{ tema.stats.times_played }}
|
||||
{% if tema.stats.times_played == 1 %}
|
||||
@@ -20,27 +46,15 @@
|
||||
No s'ha tocat mai
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if tema.properties %}
|
||||
<ul class="flex flex-wrap text-sm px-3">
|
||||
{% for property in tema.properties %}
|
||||
<button class="bg-beige text-white rounded
|
||||
m-1 px-2"
|
||||
hx-get="/api/content/temes"
|
||||
hx-target="#content"
|
||||
hx-include="[name=query]"
|
||||
hx-vars="properties:{{ add_property_str(property.value) }}"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
{{ property.value }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if tema.main_score() and tema.main_score().preview_url %}
|
||||
<a href="/tema/{{ tema.id }}">
|
||||
<img class="px-4"
|
||||
src="{{ tema.main_score().preview_url }}" />
|
||||
</a>
|
||||
<div class="flex-1 grow"></div>
|
||||
<div class="flex flex-row items-center
|
||||
row-0 px-4 text-sm text-gray-400">
|
||||
{% if tema.stats and tema.stats.sessions_played %}
|
||||
<i class="mx-1">{% include "icons/calendar.svg" %}</i>
|
||||
{% set dn = get_date_names(tema.stats.sessions_played[0].date) %}
|
||||
{{ dn.day }} {{ dn.month_name }} de {{ dn.year }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -12,38 +12,14 @@
|
||||
<i>{{ query }}</i>
|
||||
</button>
|
||||
{% endif %}
|
||||
<ul class="flex flex-wrap justify-center my-4">
|
||||
{% for property in properties %}
|
||||
<li>
|
||||
<button class="bg-beige rounded
|
||||
text-white
|
||||
m-1 px-2"
|
||||
hx-get="/api/content/temes"
|
||||
hx-target="#content"
|
||||
hx-include="[name=query]"
|
||||
hx-vars="properties:{{ remove_property_str(property) }}"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
{{ property }}
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% for property in property_results %}
|
||||
<li>
|
||||
<button class="border border-beige rounded
|
||||
text-white
|
||||
m-1 px-2"
|
||||
hx-get="/api/content/temes"
|
||||
hx-target="#content"
|
||||
hx-vars="properties:{{ add_property_str(property) }}"
|
||||
hx-swap="innerHTML"
|
||||
>
|
||||
{{ property }}
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<hr class="h-px mt-1 mb-3 bg-beige border-0">
|
||||
|
||||
<div class="mx-4 flex flex-col">
|
||||
{% include "fragments/temes/search_params/order_by.html" %}
|
||||
{% include "fragments/temes/search_params/filter.html" %}
|
||||
</div>
|
||||
|
||||
<hr class="h-px mt-3 mb-3 bg-beige border-0">
|
||||
<ul class="min-w-full w-full">
|
||||
{% for tema in temes %}
|
||||
{% include "fragments/temes/result.html" %}
|
||||
@@ -52,20 +28,22 @@
|
||||
{% if prev_offset is not none or next_offset is not none %}
|
||||
<div class="py-2">
|
||||
{% if prev_offset is not none %}
|
||||
{% set hx_vars = build_hx_vars({"properties": properties_str, "offset": prev_offset}, order_params_dict) %}
|
||||
<button class="text-beige mx-2"
|
||||
hx-get="/api/temes/busca"
|
||||
hx-include="[name=query]"
|
||||
hx-vars="properties:{{ properties_str }},offset:{{ prev_offset }}"
|
||||
hx-vars="{{ hx_vars }}"
|
||||
hx-target="#search-results"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fa fa-chevron-left" aria-hidden="true"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if next_offset is not none %}
|
||||
{% set hx_vars = build_hx_vars({"properties": properties_str, "offset": next_offset}, order_params_dict) %}
|
||||
<button class="text-beige mx-2"
|
||||
hx-get="/api/temes/busca"
|
||||
hx-include="[name=query]"
|
||||
hx-vars="properties:{{ properties_str }},offset:{{ next_offset }}"
|
||||
hx-vars="{{ hx_vars }}"
|
||||
hx-target="#search-results"
|
||||
hx-swap="outerHTML">
|
||||
<i class="fa fa-chevron-right" aria-hidden="true"></i>
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{% if properties or property_results %}
|
||||
<div class="flex flex-row items-center">
|
||||
<p class="pr-2 py-2
|
||||
{% if properties %} text-beige {% else %} text-gray-400 {% endif %}
|
||||
">Filtres:</p>
|
||||
<ul class="flex flex-wrap justify-center my-1">
|
||||
{% for property in properties %}
|
||||
<li>
|
||||
{% set hx_vars = build_hx_vars({"properties": remove_property_str(property)}, order_params_dict) %}
|
||||
<button class="bg-beige rounded
|
||||
text-white
|
||||
m-1 px-2"
|
||||
hx-get="/api/content/temes"
|
||||
hx-target="#content"
|
||||
hx-include="[name=query]"
|
||||
hx-vars="{{ hx_vars }}"
|
||||
hx-swap="innerhtml"
|
||||
>
|
||||
{{ property }}
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% for property in property_results %}
|
||||
<li>
|
||||
{% set hx_vars = build_hx_vars({"properties": add_property_str(property)}, order_params_dict) %}
|
||||
<button class="border border-beige rounded
|
||||
text-white
|
||||
m-1 px-2"
|
||||
hx-get="/api/content/temes"
|
||||
hx-target="#content"
|
||||
hx-vars="{{ hx_vars }}"
|
||||
hx-swap="innerhtml"
|
||||
>
|
||||
{{ property }}
|
||||
</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,67 @@
|
||||
<div class="flex flex-row items-center justify-center">
|
||||
{% set times_played_color = "text-gray-400" %}
|
||||
{% set times_played_caret = "off" %}
|
||||
{% set times_played_target_params = {"order_by": "'times_played'", "order": "'desc'"} %}
|
||||
|
||||
{% set last_played_color = "text-gray-400" %}
|
||||
{% set last_played_caret = "off" %}
|
||||
{% set last_played_target_params = {"order_by": "'last_played'", "order": "'desc'"} %}
|
||||
|
||||
{% if order_params is not none %}
|
||||
{% if order_params.order_by == OrderBy.TIMES_PLAYED %}
|
||||
{% set times_played_color = "text-beige" %}
|
||||
{% if order_params.order == Order.ASC %}
|
||||
{% set times_played_caret = "asc" %}
|
||||
{% set times_played_target_params = {} %}
|
||||
{% elif order_params.order == Order.DESC %}
|
||||
{% set times_played_caret = "desc" %}
|
||||
{% set times_played_target_params = {"order_by": "'times_played'", "order": "'asc'"} %}
|
||||
{% endif %}
|
||||
{% elif order_params.order_by == OrderBy.LAST_PLAYED %}
|
||||
{% set last_played_color = "text-beige" %}
|
||||
{% if order_params.order == Order.ASC %}
|
||||
{% set last_played_caret = "asc" %}
|
||||
{% set last_played_target_params = {} %}
|
||||
{% elif order_params.order == Order.DESC %}
|
||||
{% set last_played_caret = "desc" %}
|
||||
{% set last_played_target_params = {"order_by": "'last_played'", "order": "'asc'"} %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% set hx_vars = build_hx_vars({"properties": properties_str}, times_played_target_params) %}
|
||||
<button title="Ordena per cops tocat"
|
||||
class="{{ times_played_color }} mx-2 flex flex-row"
|
||||
hx-get="/api/temes/busca"
|
||||
hx-include="[name=query]"
|
||||
hx-vars="{{ hx_vars }}"
|
||||
hx-target="#search-results"
|
||||
hx-swap="outerHTML">
|
||||
<i class="mr-1 flex-none">{% include "icons/music-box.svg" %}</i>
|
||||
{% if times_played_caret == "asc" %}
|
||||
<i class="fa fa-caret-up flex-1 w-0" aria-hidden="true"></i>
|
||||
{% elif times_played_caret == "desc" %}
|
||||
<i class="fa fa-caret-down flex-1 w-0" aria-hidden="true"></i>
|
||||
{% else %}
|
||||
<div class="flex-1 w-0"></div>
|
||||
{% endif %}
|
||||
</button>
|
||||
|
||||
{% set hx_vars = build_hx_vars({"properties": properties_str}, last_played_target_params) %}
|
||||
<button title="Ordena per últim cop tocat"
|
||||
class="{{ last_played_color }} mx-2 flex flex-row"
|
||||
hx-get="/api/temes/busca"
|
||||
hx-include="[name=query]"
|
||||
hx-vars="{{ hx_vars }}"
|
||||
hx-target="#search-results"
|
||||
hx-swap="outerHTML">
|
||||
<i class="mr-1">{% include "icons/calendar.svg" %}</i>
|
||||
{% if last_played_caret == "asc" %}
|
||||
<i class="fa fa-caret-up flex-1 w-0" aria-hidden="true"></i>
|
||||
{% elif last_played_caret == "desc" %}
|
||||
<i class="fa fa-caret-down flex-1 w-0" aria-hidden="true"></i>
|
||||
{% else %}
|
||||
<div class="flex-1 w-0"></div>
|
||||
{% endif %}
|
||||
</button>
|
||||
</div>
|
||||
19
folkugat_web/assets/templates/icons/calendar.svg
Normal file
19
folkugat_web/assets/templates/icons/calendar.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-spotify"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<path
|
||||
d="M 5.6841749,7.1762614 V 8.7837442 H 4.0766921 V 7.1762614 Z m 3.1766921,0 V 8.7837442 H 7.2916577 V 7.1762614 Z m 3.214966,0 V 8.7837442 H 10.46835 V 7.1762614 Z m 1.569209,-5.5687796 q 0.669784,0 1.129065,0.4784175 0.478418,0.4592808 0.478418,1.1290653 V 14.352524 q 0,0.669784 -0.478418,1.129065 -0.459281,0.478418 -1.129065,0.478418 H 2.5074827 q -0.6697844,0 -1.1482019,-0.478418 Q 0.89999998,15.022308 0.89999998,14.352524 V 3.2149646 q 0,-0.6697845 0.45928082,-1.1290653 Q 1.8376983,1.6074818 2.5074827,1.6074818 H 3.2920874 V -9.4223022e-7 H 4.8995702 V 1.6074818 H 11.252955 V -9.4223022e-7 h 1.607482 V 1.6074818 Z m 0,12.7450422 V 5.6070521 H 2.5074827 V 14.352524 Z M 5.6841749,10.391227 v 1.569209 H 4.0766921 v -1.569209 z m 3.1766921,0 v 1.569209 H 7.2916577 v -1.569209 z m 3.214966,0 v 1.569209 H 10.46835 v -1.569209 z"
|
||||
id="text50"
|
||||
style="font-size:19.1367px;line-height:1.25;stroke-width:1.4355"
|
||||
aria-label="" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
19
folkugat_web/assets/templates/icons/music-box.svg
Normal file
19
folkugat_web/assets/templates/icons/music-box.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
fill="currentColor"
|
||||
class="bi bi-spotify"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs1" />
|
||||
<path
|
||||
d="M 1.5769257,3.1922874 H 0 V 14.423075 q 0,0.634616 0.46153922,1.096156 0.48077001,0.48077 1.11538648,0.48077 H 12.807713 V 14.423075 H 1.5769257 Z M 12.807713,3.9807502 h -2.40385 v 4.4230842 q 0,0.8269244 -0.5961547,1.4230792 -0.576924,0.5769244 -1.4038484,0.5769244 -0.8076937,0 -1.4230793,-0.5769244 -0.5961548,-0.5961548 -0.5961548,-1.4038484 0,-0.8269244 0.5961548,-1.4230793 Q 7.5961662,6.3846003 8.4038599,6.3846003 9.0384763,6.3653695 9.6154003,6.7884471 V 2.4038246 H 12.807713 Z M 14.4231,-2.5497437e-5 H 4.8077002 q -0.6730781,0 -1.1538481,0.480770017437 Q 3.1923129,0.94228373 3.1923129,1.5769002 V 11.1923 q 0,0.673078 0.4615392,1.153849 0.48077,0.461539 1.1538481,0.461539 H 14.4231 q 0.634617,0 1.096156,-0.461539 0.48077,-0.480771 0.48077,-1.153849 V 1.5769002 q 0,-0.63461647 -0.48077,-1.09615568 Q 15.057717,-2.5497437e-5 14.4231,-2.5497437e-5 Z"
|
||||
id="text48"
|
||||
style="font-size:19.2308px;line-height:1.25;stroke-width:1.44231"
|
||||
aria-label="" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -5,7 +5,9 @@ from fastapi import Request
|
||||
from folkugat_web.model import temes as model
|
||||
from folkugat_web.model.lilypond.processing import RenderError
|
||||
from folkugat_web.model.pagines import Pages
|
||||
from folkugat_web.model.search import Order, OrderBy, OrderParams
|
||||
from folkugat_web.services import sessions as sessions_service
|
||||
from folkugat_web.services.sessions import get_date_names
|
||||
from folkugat_web.services.temes import query as temes_q
|
||||
from folkugat_web.services.temes import scores as scores_service
|
||||
from folkugat_web.services.temes import search as temes_s
|
||||
@@ -13,20 +15,48 @@ from folkugat_web.templates import templates
|
||||
from folkugat_web.utils import FnChain
|
||||
|
||||
|
||||
def build_temes_params(query: str, properties: list[str]) -> str:
|
||||
def build_temes_params(
|
||||
query: str,
|
||||
properties: list[str],
|
||||
order_params: OrderParams | None = None,
|
||||
) -> str:
|
||||
content_params = [
|
||||
("query", query),
|
||||
*[("properties", prop) for prop in properties or []]
|
||||
]
|
||||
if order_params:
|
||||
content_params.append(("order_by", order_params.order_by.value))
|
||||
content_params.append(("order", order_params.order.value))
|
||||
return urllib.parse.urlencode(content_params)
|
||||
|
||||
|
||||
def build_hx_vars(*vars_dicts: dict[str, str]) -> str:
|
||||
result = ",".join(f"{var}:{value}" for hx_vars in vars_dicts for var, value in hx_vars.items())
|
||||
return result
|
||||
|
||||
|
||||
def build_property_str(properties: list[str]) -> str:
|
||||
return json.dumps(properties).replace("'", "\\'").replace('"', "'")
|
||||
|
||||
|
||||
def temes_pagina(request: Request, logged_in: bool, query: str, properties: list[str]):
|
||||
def build_order_params_dict(order_params: OrderParams | None) -> dict[str, str]:
|
||||
if not order_params:
|
||||
return {}
|
||||
return {
|
||||
"order_by": f"'{order_params.order_by.value}'",
|
||||
"order": f"'{order_params.order.value}'",
|
||||
}
|
||||
|
||||
|
||||
def temes_pagina(
|
||||
request: Request,
|
||||
logged_in: bool,
|
||||
query: str,
|
||||
properties: list[str],
|
||||
order_params: OrderParams | None,
|
||||
):
|
||||
properties_str = build_property_str(properties)
|
||||
order_params_dict = build_order_params_dict(order_params)
|
||||
return templates.TemplateResponse(
|
||||
"fragments/temes/pagina.html",
|
||||
{
|
||||
@@ -35,8 +65,14 @@ def temes_pagina(request: Request, logged_in: bool, query: str, properties: list
|
||||
"query": query,
|
||||
"properties": properties,
|
||||
"properties_str": properties_str,
|
||||
"order_params": order_params,
|
||||
"order_params_dict": order_params_dict,
|
||||
"build_hx_vars": build_hx_vars,
|
||||
"property_results": [],
|
||||
"Order": Order,
|
||||
"OrderBy": OrderBy,
|
||||
"Pages": Pages,
|
||||
"get_date_names": get_date_names,
|
||||
"menu_selected_id": Pages.Temes,
|
||||
}
|
||||
)
|
||||
@@ -47,12 +83,14 @@ def temes_busca(
|
||||
logged_in: bool,
|
||||
query: str,
|
||||
properties: list[str],
|
||||
order_params: OrderParams | None = None,
|
||||
offset: int = 0,
|
||||
limit: int = 10,
|
||||
):
|
||||
temes = temes_s.busca_temes(
|
||||
query=query,
|
||||
properties=properties,
|
||||
order_params=order_params,
|
||||
hidden=logged_in,
|
||||
limit=limit + 1,
|
||||
offset=offset,
|
||||
@@ -78,7 +116,12 @@ def temes_busca(
|
||||
if prop not in properties
|
||||
]
|
||||
|
||||
temes_url = f"/temes?{build_temes_params(query, properties)}"
|
||||
temes_params = build_temes_params(
|
||||
query=query,
|
||||
properties=properties,
|
||||
order_params=order_params,
|
||||
)
|
||||
temes_url = f"/temes?{temes_params}"
|
||||
|
||||
def _add_property_str(prop: str) -> str:
|
||||
properties_dedup = list(set(properties + [prop]))
|
||||
@@ -97,13 +140,19 @@ def temes_busca(
|
||||
"query": query,
|
||||
"properties": properties,
|
||||
"properties_str": build_property_str(properties),
|
||||
"order_params": order_params,
|
||||
"order_params_dict": build_order_params_dict(order_params),
|
||||
"property_results": property_results,
|
||||
"prev_offset": prev_offset,
|
||||
"next_offset": next_offset,
|
||||
"LinkType": model.LinkType,
|
||||
"ContentType": model.ContentType,
|
||||
"Order": Order,
|
||||
"OrderBy": OrderBy,
|
||||
"get_date_names": get_date_names,
|
||||
"add_property_str": _add_property_str,
|
||||
"remove_property_str": _remove_property_str,
|
||||
"build_hx_vars": build_hx_vars,
|
||||
},
|
||||
headers={"HX-Replace-Url": temes_url},
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import dataclasses
|
||||
import enum
|
||||
from collections.abc import Iterable
|
||||
from typing import Generic, Self, TypeVar
|
||||
|
||||
@@ -6,6 +7,22 @@ T = TypeVar("T")
|
||||
NGrams = dict[int, list[str]]
|
||||
|
||||
|
||||
class Order(enum.Enum):
|
||||
ASC = "asc"
|
||||
DESC = "desc"
|
||||
|
||||
|
||||
class OrderBy(enum.Enum):
|
||||
TIMES_PLAYED = "times_played"
|
||||
LAST_PLAYED = "last_played"
|
||||
|
||||
|
||||
@dataclasses.dataclass(frozen=True)
|
||||
class OrderParams:
|
||||
order_by: OrderBy
|
||||
order: Order
|
||||
|
||||
|
||||
@dataclasses.dataclass(order=True)
|
||||
class SearchMatch:
|
||||
distance: float
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import datetime
|
||||
import time
|
||||
from collections.abc import Iterable, Iterator
|
||||
from sqlite3 import Connection
|
||||
@@ -13,7 +14,7 @@ from folkugat_web.model import search as search_model
|
||||
from folkugat_web.model import temes as model
|
||||
from folkugat_web.services.temes import properties as properties_service
|
||||
from folkugat_web.services.temes import query as query_service
|
||||
from folkugat_web.utils import FnChain
|
||||
from folkugat_web.utils import FnChain, identity
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
@@ -69,6 +70,40 @@ def _sort_by_distance(qrs: Iterable[search_model.QueryResult[T]]) -> list[search
|
||||
return sorted(qrs, key=lambda qr: qr.distance)
|
||||
|
||||
|
||||
def _sort_by_times_played_fn(order: search_model.Order) -> Callable[[list[model.Tema]], list[model.Tema]]:
|
||||
reverse = order is search_model.Order.DESC
|
||||
|
||||
def _sort_by_times_played(temes: list[model.Tema]) -> list[model.Tema]:
|
||||
return sorted(temes, key=lambda tema: tema.stats.times_played if tema.stats else 0, reverse=reverse)
|
||||
|
||||
return _sort_by_times_played
|
||||
|
||||
|
||||
def _sort_by_last_played_fn(order: search_model.Order) -> Callable[[list[model.Tema]], list[model.Tema]]:
|
||||
reverse = order is search_model.Order.DESC
|
||||
|
||||
def _tune_last_played_key(tema: model.Tema) -> datetime.date:
|
||||
if not tema.stats or not tema.stats.sessions_played:
|
||||
return datetime.date.min
|
||||
else:
|
||||
return tema.stats.sessions_played[0].date
|
||||
|
||||
def _sort_by_last_played(temes: list[model.Tema]) -> list[model.Tema]:
|
||||
return sorted(temes, key=_tune_last_played_key, reverse=reverse)
|
||||
|
||||
return _sort_by_last_played
|
||||
|
||||
|
||||
def _build_sort_fn(order_params: search_model.OrderParams | None) -> Callable[[list[model.Tema]], list[model.Tema]]:
|
||||
if order_params is None:
|
||||
return identity
|
||||
match order_params.order_by:
|
||||
case search_model.OrderBy.TIMES_PLAYED:
|
||||
return _sort_by_times_played_fn(order=order_params.order)
|
||||
case search_model.OrderBy.LAST_PLAYED:
|
||||
return _sort_by_last_played_fn(order=order_params.order)
|
||||
|
||||
|
||||
def _query_results_to_temes(
|
||||
con: Connection
|
||||
) -> Callable[[Iterable[search_model.QueryResult[int]]], Iterator[model.Tema]]:
|
||||
@@ -105,6 +140,7 @@ def _apply_limit_offset(limit: int, offset: int) -> Callable[[Iterable[T]], list
|
||||
def busca_temes(
|
||||
query: str,
|
||||
properties: list[str],
|
||||
order_params: search_model.OrderParams | None = None,
|
||||
hidden: bool = False,
|
||||
limit: int = 10,
|
||||
offset: int = 0,
|
||||
@@ -124,6 +160,7 @@ def busca_temes(
|
||||
properties_service.add_properties_to_temes |
|
||||
_filter_properties(properties) |
|
||||
query_service.temes_compute_stats |
|
||||
_build_sort_fn(order_params=order_params) |
|
||||
_apply_limit_offset(limit=limit, offset=offset)
|
||||
).result()
|
||||
logger.info(f"Temes search time: { int((time.time() - t0) * 1000) } ms")
|
||||
|
||||
@@ -25,6 +25,10 @@ def groupby(
|
||||
yield k, group_fn(g)
|
||||
|
||||
|
||||
def identity(t: T) -> T:
|
||||
return t
|
||||
|
||||
|
||||
U = TypeVar("U")
|
||||
V = TypeVar("V")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user