geo-activity-playground 0.37.0__py3-none-any.whl → 0.38.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geo_activity_playground/core/activities.py +12 -0
- geo_activity_playground/core/config.py +6 -2
- geo_activity_playground/core/enrichment.py +9 -0
- geo_activity_playground/core/meta_search.py +49 -9
- geo_activity_playground/core/summary_stats.py +1 -1
- geo_activity_playground/core/test_meta_search.py +9 -0
- geo_activity_playground/webui/activity/controller.py +20 -0
- geo_activity_playground/webui/activity/templates/activity/day.html.j2 +3 -10
- geo_activity_playground/webui/activity/templates/activity/name.html.j2 +2 -0
- geo_activity_playground/webui/activity/templates/activity/show.html.j2 +17 -0
- geo_activity_playground/webui/app.py +18 -5
- geo_activity_playground/webui/eddington_blueprint.py +162 -58
- geo_activity_playground/webui/equipment_blueprint.py +12 -3
- geo_activity_playground/webui/explorer/blueprint.py +4 -0
- geo_activity_playground/webui/heatmap/blueprint.py +3 -0
- geo_activity_playground/webui/heatmap/templates/heatmap/index.html.j2 +1 -1
- geo_activity_playground/webui/search_blueprint.py +34 -2
- geo_activity_playground/webui/search_util.py +42 -9
- geo_activity_playground/webui/summary_blueprint.py +31 -34
- geo_activity_playground/webui/templates/eddington/index.html.j2 +69 -2
- geo_activity_playground/webui/templates/search/index.html.j2 +8 -4
- geo_activity_playground/webui/templates/search_form.html.j2 +91 -57
- geo_activity_playground/webui/templates/summary/index.html.j2 +1 -1
- {geo_activity_playground-0.37.0.dist-info → geo_activity_playground-0.38.0.dist-info}/METADATA +1 -1
- {geo_activity_playground-0.37.0.dist-info → geo_activity_playground-0.38.0.dist-info}/RECORD +28 -28
- {geo_activity_playground-0.37.0.dist-info → geo_activity_playground-0.38.0.dist-info}/LICENSE +0 -0
- {geo_activity_playground-0.37.0.dist-info → geo_activity_playground-0.38.0.dist-info}/WHEEL +0 -0
- {geo_activity_playground-0.37.0.dist-info → geo_activity_playground-0.38.0.dist-info}/entry_points.txt +0 -0
@@ -6,6 +6,7 @@ from flask import render_template
|
|
6
6
|
from geo_activity_playground.core.activities import ActivityRepository
|
7
7
|
from geo_activity_playground.core.config import Config
|
8
8
|
from geo_activity_playground.core.summary_stats import get_equipment_use_table
|
9
|
+
from geo_activity_playground.webui.plot_util import make_kind_scale
|
9
10
|
|
10
11
|
|
11
12
|
def make_equipment_blueprint(
|
@@ -20,7 +21,7 @@ def make_equipment_blueprint(
|
|
20
21
|
)
|
21
22
|
|
22
23
|
equipment_variables = {}
|
23
|
-
for equipment in
|
24
|
+
for equipment in equipment_summary["equipment"]:
|
24
25
|
selection = repository.meta.loc[repository.meta["equipment"] == equipment]
|
25
26
|
total_distances = pd.DataFrame(
|
26
27
|
{
|
@@ -55,6 +56,11 @@ def make_equipment_blueprint(
|
|
55
56
|
.encode(
|
56
57
|
alt.X("year(start):O", title="Year"),
|
57
58
|
alt.Y("sum(distance_km)", title="Distance / km"),
|
59
|
+
alt.Color(
|
60
|
+
"kind",
|
61
|
+
scale=make_kind_scale(repository.meta, config),
|
62
|
+
title="Kind",
|
63
|
+
),
|
58
64
|
)
|
59
65
|
.to_json(format="vega")
|
60
66
|
)
|
@@ -67,7 +73,10 @@ def make_equipment_blueprint(
|
|
67
73
|
)
|
68
74
|
.mark_bar()
|
69
75
|
.encode(
|
70
|
-
alt.X(
|
76
|
+
alt.X(
|
77
|
+
"kind",
|
78
|
+
title="Kind",
|
79
|
+
),
|
71
80
|
alt.Y("sum(distance_km)", title="Distance / km"),
|
72
81
|
)
|
73
82
|
.to_json(format="vega")
|
@@ -84,7 +93,7 @@ def make_equipment_blueprint(
|
|
84
93
|
|
85
94
|
variables = {
|
86
95
|
"equipment_variables": equipment_variables,
|
87
|
-
"equipment_summary": equipment_summary,
|
96
|
+
"equipment_summary": equipment_summary.to_dict(orient="records"),
|
88
97
|
}
|
89
98
|
|
90
99
|
return render_template("equipment/index.html.j2", **variables)
|
@@ -4,11 +4,14 @@ from flask import render_template
|
|
4
4
|
from flask import Response
|
5
5
|
from flask import url_for
|
6
6
|
|
7
|
+
from geo_activity_playground.webui.authenticator import Authenticator
|
8
|
+
from geo_activity_playground.webui.authenticator import needs_authentication
|
7
9
|
from geo_activity_playground.webui.explorer.controller import ExplorerController
|
8
10
|
|
9
11
|
|
10
12
|
def make_explorer_blueprint(
|
11
13
|
explorer_controller: ExplorerController,
|
14
|
+
authenticator: Authenticator,
|
12
15
|
) -> Blueprint:
|
13
16
|
blueprint = Blueprint("explorer", __name__, template_folder="templates")
|
14
17
|
|
@@ -19,6 +22,7 @@ def make_explorer_blueprint(
|
|
19
22
|
)
|
20
23
|
|
21
24
|
@blueprint.route("/enable-zoom-level/<zoom>")
|
25
|
+
@needs_authentication(authenticator)
|
22
26
|
def enable_zoom_level(zoom: str):
|
23
27
|
explorer_controller.enable_zoom_level(int(zoom))
|
24
28
|
return redirect(url_for(".map", zoom=zoom))
|
@@ -9,12 +9,14 @@ from geo_activity_playground.core.config import Config
|
|
9
9
|
from geo_activity_playground.explorer.tile_visits import TileVisitAccessor
|
10
10
|
from geo_activity_playground.webui.heatmap.heatmap_controller import HeatmapController
|
11
11
|
from geo_activity_playground.webui.search_util import search_query_from_form
|
12
|
+
from geo_activity_playground.webui.search_util import SearchQueryHistory
|
12
13
|
|
13
14
|
|
14
15
|
def make_heatmap_blueprint(
|
15
16
|
repository: ActivityRepository,
|
16
17
|
tile_visit_accessor: TileVisitAccessor,
|
17
18
|
config: Config,
|
19
|
+
search_query_history: SearchQueryHistory,
|
18
20
|
) -> Blueprint:
|
19
21
|
heatmap_controller = HeatmapController(repository, tile_visit_accessor, config)
|
20
22
|
blueprint = Blueprint("heatmap", __name__, template_folder="templates")
|
@@ -22,6 +24,7 @@ def make_heatmap_blueprint(
|
|
22
24
|
@blueprint.route("/")
|
23
25
|
def index():
|
24
26
|
query = search_query_from_form(request.args)
|
27
|
+
search_query_history.register_query(query)
|
25
28
|
return render_template(
|
26
29
|
"heatmap/index.html.j2", **heatmap_controller.render(query)
|
27
30
|
)
|
@@ -1,16 +1,22 @@
|
|
1
|
+
import urllib.parse
|
1
2
|
from functools import reduce
|
2
3
|
|
3
4
|
import dateutil.parser
|
4
5
|
from flask import Blueprint
|
5
6
|
from flask import flash
|
7
|
+
from flask import redirect
|
6
8
|
from flask import render_template
|
7
9
|
from flask import request
|
8
10
|
from flask import Response
|
9
11
|
|
10
12
|
from ..core.activities import ActivityRepository
|
13
|
+
from geo_activity_playground.core.config import ConfigAccessor
|
11
14
|
from geo_activity_playground.core.meta_search import apply_search_query
|
12
15
|
from geo_activity_playground.core.meta_search import SearchQuery
|
16
|
+
from geo_activity_playground.webui.authenticator import Authenticator
|
17
|
+
from geo_activity_playground.webui.authenticator import needs_authentication
|
13
18
|
from geo_activity_playground.webui.search_util import search_query_from_form
|
19
|
+
from geo_activity_playground.webui.search_util import SearchQueryHistory
|
14
20
|
|
15
21
|
|
16
22
|
def reduce_or(selections):
|
@@ -21,18 +27,44 @@ def reduce_and(selections):
|
|
21
27
|
return reduce(lambda a, b: a & b, selections)
|
22
28
|
|
23
29
|
|
24
|
-
def make_search_blueprint(
|
30
|
+
def make_search_blueprint(
|
31
|
+
repository: ActivityRepository,
|
32
|
+
search_query_history: SearchQueryHistory,
|
33
|
+
authenticator: Authenticator,
|
34
|
+
config_accessor: ConfigAccessor,
|
35
|
+
) -> Blueprint:
|
25
36
|
blueprint = Blueprint("search", __name__, template_folder="templates")
|
26
37
|
|
27
38
|
@blueprint.route("/")
|
28
39
|
def index():
|
29
40
|
query = search_query_from_form(request.args)
|
41
|
+
search_query_history.register_query(query)
|
30
42
|
activities = apply_search_query(repository.meta, query)
|
31
43
|
|
32
44
|
return render_template(
|
33
45
|
"search/index.html.j2",
|
34
|
-
activities=list(activities.iterrows()),
|
46
|
+
activities=reversed(list(activities.iterrows())),
|
35
47
|
query=query.to_jinja(),
|
36
48
|
)
|
37
49
|
|
50
|
+
@blueprint.route("/save-search-query")
|
51
|
+
@needs_authentication(authenticator)
|
52
|
+
def save_search_query():
|
53
|
+
query = search_query_from_form(request.args)
|
54
|
+
primitives = query.to_primitives()
|
55
|
+
if primitives not in config_accessor().search_queries_favorites:
|
56
|
+
config_accessor().search_queries_favorites.append(primitives)
|
57
|
+
config_accessor.save()
|
58
|
+
return redirect(urllib.parse.unquote_plus(request.args["redirect"]))
|
59
|
+
|
60
|
+
@blueprint.route("/delete-search-query")
|
61
|
+
@needs_authentication(authenticator)
|
62
|
+
def delete_search_query():
|
63
|
+
query = search_query_from_form(request.args)
|
64
|
+
primitives = query.to_primitives()
|
65
|
+
if primitives in config_accessor().search_queries_favorites:
|
66
|
+
config_accessor().search_queries_favorites.remove(primitives)
|
67
|
+
config_accessor.save()
|
68
|
+
return redirect(urllib.parse.unquote_plus(request.args["redirect"]))
|
69
|
+
|
38
70
|
return blueprint
|
@@ -1,10 +1,11 @@
|
|
1
|
-
import datetime
|
2
1
|
from typing import Optional
|
3
2
|
|
4
|
-
import dateutil.parser
|
5
3
|
from werkzeug.datastructures import MultiDict
|
6
4
|
|
5
|
+
from geo_activity_playground.core.config import ConfigAccessor
|
6
|
+
from geo_activity_playground.core.meta_search import _parse_date_or_none
|
7
7
|
from geo_activity_playground.core.meta_search import SearchQuery
|
8
|
+
from geo_activity_playground.webui.authenticator import Authenticator
|
8
9
|
|
9
10
|
|
10
11
|
def search_query_from_form(args: MultiDict) -> SearchQuery:
|
@@ -20,12 +21,44 @@ def search_query_from_form(args: MultiDict) -> SearchQuery:
|
|
20
21
|
return query
|
21
22
|
|
22
23
|
|
23
|
-
def _parse_date_or_none(s: Optional[str]) -> Optional[datetime.date]:
|
24
|
-
if not s:
|
25
|
-
return None
|
26
|
-
else:
|
27
|
-
return dateutil.parser.parse(s).date()
|
28
|
-
|
29
|
-
|
30
24
|
def _parse_bool(s: str) -> bool:
|
31
25
|
return s == "true"
|
26
|
+
|
27
|
+
|
28
|
+
class SearchQueryHistory:
|
29
|
+
def __init__(
|
30
|
+
self, config_accessor: ConfigAccessor, authenticator: Authenticator
|
31
|
+
) -> None:
|
32
|
+
self._config_accessor = config_accessor
|
33
|
+
self._authenticator = authenticator
|
34
|
+
|
35
|
+
def register_query(self, search_query: SearchQuery) -> None:
|
36
|
+
if not self._authenticator.is_authenticated():
|
37
|
+
return
|
38
|
+
|
39
|
+
if not search_query.active:
|
40
|
+
return
|
41
|
+
|
42
|
+
primitives = search_query.to_primitives()
|
43
|
+
while primitives in self._config_accessor().search_queries_last:
|
44
|
+
self._config_accessor().search_queries_last.remove(primitives)
|
45
|
+
self._config_accessor().search_queries_last.append(primitives)
|
46
|
+
while (
|
47
|
+
len(self._config_accessor().search_queries_last)
|
48
|
+
> self._config_accessor().search_queries_num_keep
|
49
|
+
):
|
50
|
+
self._config_accessor().search_queries_last.pop(0)
|
51
|
+
self._config_accessor.save()
|
52
|
+
|
53
|
+
def prepare_favorites(self) -> list[dict]:
|
54
|
+
return self._prepare_list(self._config_accessor().search_queries_favorites)
|
55
|
+
|
56
|
+
def prepare_last(self) -> list[dict]:
|
57
|
+
return self._prepare_list(self._config_accessor().search_queries_last)
|
58
|
+
|
59
|
+
def _prepare_list(self, l: list[dict]) -> list[tuple[str, dict]]:
|
60
|
+
result = []
|
61
|
+
for elem in l:
|
62
|
+
search_query = SearchQuery.from_primitives(elem)
|
63
|
+
result.append((str(search_query), search_query.to_url_str()))
|
64
|
+
return result
|
@@ -13,14 +13,20 @@ from geo_activity_playground.core.config import Config
|
|
13
13
|
from geo_activity_playground.core.meta_search import apply_search_query
|
14
14
|
from geo_activity_playground.webui.plot_util import make_kind_scale
|
15
15
|
from geo_activity_playground.webui.search_util import search_query_from_form
|
16
|
+
from geo_activity_playground.webui.search_util import SearchQueryHistory
|
16
17
|
|
17
18
|
|
18
|
-
def make_summary_blueprint(
|
19
|
+
def make_summary_blueprint(
|
20
|
+
repository: ActivityRepository,
|
21
|
+
config: Config,
|
22
|
+
search_query_history: SearchQueryHistory,
|
23
|
+
) -> Blueprint:
|
19
24
|
blueprint = Blueprint("summary", __name__, template_folder="templates")
|
20
25
|
|
21
26
|
@blueprint.route("/")
|
22
27
|
def index():
|
23
28
|
query = search_query_from_form(request.args)
|
29
|
+
search_query_history.register_query(query)
|
24
30
|
activities = apply_search_query(repository.meta, query)
|
25
31
|
|
26
32
|
kind_scale = make_kind_scale(repository.meta, config)
|
@@ -63,44 +69,35 @@ def make_summary_blueprint(repository: ActivityRepository, config: Config) -> Bl
|
|
63
69
|
def nominate_activities(meta: pd.DataFrame) -> dict[int, list[str]]:
|
64
70
|
nominations: dict[int, list[str]] = collections.defaultdict(list)
|
65
71
|
|
66
|
-
|
67
|
-
nominations[i].append(f"Greatest distance: {meta.loc[i].distance_km:.1f} km")
|
72
|
+
_nominate_activities_inner(meta, "", nominations)
|
68
73
|
|
69
|
-
|
70
|
-
|
74
|
+
for kind, group in meta.groupby("kind"):
|
75
|
+
_nominate_activities_inner(group, f" for {kind}", nominations)
|
76
|
+
for equipment, group in meta.groupby("equipment"):
|
77
|
+
_nominate_activities_inner(group, f" with {equipment}", nominations)
|
71
78
|
|
72
|
-
|
73
|
-
i = meta["calories"].idxmax()
|
74
|
-
nominations[i].append(f"Most calories burnt: {meta.loc[i].calories:.0f} kcal")
|
79
|
+
return nominations
|
75
80
|
|
76
|
-
if "steps" in meta.columns and not pd.isna(meta["steps"]).all():
|
77
|
-
i = meta["steps"].idxmax()
|
78
|
-
nominations[i].append(f"Most steps: {meta.loc[i].steps:.0f}")
|
79
81
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
lambda row: f"Most calories burnt for {row.kind}: {row.calories:.0f} kcal",
|
93
|
-
),
|
94
|
-
("steps", lambda row: f"Most steps for {row.kind}: {row.steps:.0f}"),
|
95
|
-
]:
|
96
|
-
if key in group.columns:
|
97
|
-
series = group[key]
|
98
|
-
if not pd.isna(series).all():
|
99
|
-
i = series.idxmax()
|
100
|
-
if not pd.isna(i):
|
101
|
-
nominations[i].append(text(meta.loc[i]))
|
82
|
+
def _nominate_activities_inner(
|
83
|
+
meta: pd.DataFrame, title_suffix: str, nominations: dict[int, list[str]]
|
84
|
+
) -> None:
|
85
|
+
ratings = [
|
86
|
+
("distance_km", "Greatest distance", "{:.1f} km"),
|
87
|
+
("elapsed_time", "Longest elapsed time", "{}"),
|
88
|
+
("average_speed_moving_kmh", "Highest average moving speed", "{:.1f} km/h"),
|
89
|
+
("average_speed_elapsed_kmh", "Highest average elapsed speed", "{:.1f} km/h"),
|
90
|
+
("calories", "Most calories burnt", "{:.0f}"),
|
91
|
+
("steps", "Most steps", "{:.0f}"),
|
92
|
+
("elevation_gain", "Largest elevation gain", "{:.0f} m"),
|
93
|
+
]
|
102
94
|
|
103
|
-
|
95
|
+
for variable, title, format_str in ratings:
|
96
|
+
if variable in meta.columns and not pd.isna(meta[variable]).all():
|
97
|
+
i = meta[variable].idxmax()
|
98
|
+
value = meta.loc[i, variable]
|
99
|
+
format_applied = format_str.format(value)
|
100
|
+
nominations[i].append(f"{title}{title_suffix}: {format_applied}")
|
104
101
|
|
105
102
|
|
106
103
|
def embellished_activities(meta: pd.DataFrame) -> pd.DataFrame:
|
@@ -5,7 +5,7 @@
|
|
5
5
|
<h1 class="mb-3">Eddington Number</h1>
|
6
6
|
|
7
7
|
<div class="mb-3">
|
8
|
-
{{ search_form(query, equipments_avail, kinds_avail) }}
|
8
|
+
{{ search_form(query, equipments_avail, kinds_avail, search_query_favorites, search_query_last, request_url) }}
|
9
9
|
</div>
|
10
10
|
|
11
11
|
<div class="row mb-3">
|
@@ -55,7 +55,74 @@
|
|
55
55
|
<p>In a graphical representation, the Eddington number is the distance where the red line intersects with the
|
56
56
|
blue area.</p>
|
57
57
|
|
58
|
-
{{ vega_direct("
|
58
|
+
{{ vega_direct("logarithmic_plot", logarithmic_plot) }}
|
59
59
|
</div>
|
60
60
|
</div>
|
61
|
+
|
62
|
+
<div class="mb-3">
|
63
|
+
<h2 class="mb-3">Eddington number history</h2>
|
64
|
+
|
65
|
+
<p>How did the Eddington number evolve over time?</p>
|
66
|
+
|
67
|
+
{{ vega_direct("eddington_number_history_plot", eddington_number_history_plot) }}
|
68
|
+
</div>
|
69
|
+
|
70
|
+
<div class="mb-3">
|
71
|
+
<h2 class="mb-3">Yearly Eddington number</h2>
|
72
|
+
|
73
|
+
<p>If we only consider the activities within one calendar year for the Eddington number, we get the following:</p>
|
74
|
+
|
75
|
+
<table class="table">
|
76
|
+
<thead>
|
77
|
+
<tr>
|
78
|
+
<th>Year</th>
|
79
|
+
<th>Eddington number</th>
|
80
|
+
</tr>
|
81
|
+
</thead>
|
82
|
+
<tbody>
|
83
|
+
{% for year, eddington in yearly_eddington.items() %}
|
84
|
+
<tr>
|
85
|
+
<td>{{ year }}</td>
|
86
|
+
<td>{{ eddington }}</td>
|
87
|
+
</tr>
|
88
|
+
{% endfor %}
|
89
|
+
</tbody>
|
90
|
+
</table>
|
91
|
+
</div>
|
92
|
+
|
93
|
+
|
94
|
+
<h2 class="mb-3">Eddington number per Week</h2>
|
95
|
+
|
96
|
+
<div class="row mb-3">
|
97
|
+
<div class="col-md-8">
|
98
|
+
{{ vega_direct("eddington_per_week_plot", eddington_per_week_plot) }}
|
99
|
+
</div>
|
100
|
+
|
101
|
+
<div class="col-md-4">
|
102
|
+
<table class="table">
|
103
|
+
<thead>
|
104
|
+
<tr>
|
105
|
+
<th>Distance / km</th>
|
106
|
+
<th>Count</th>
|
107
|
+
<th>Missing weeks</th>
|
108
|
+
</tr>
|
109
|
+
</thead>
|
110
|
+
<tbody>
|
111
|
+
{% for row in eddington_table_weeks %}
|
112
|
+
<tr>
|
113
|
+
<td>{{ row['distance_km'] }}</td>
|
114
|
+
<td>{{ row['total'] }}</td>
|
115
|
+
<td>{{ row['missing'] }}</td>
|
116
|
+
</tr>
|
117
|
+
{% endfor %}
|
118
|
+
</tbody>
|
119
|
+
</table>
|
120
|
+
</div>
|
121
|
+
</div>
|
122
|
+
|
123
|
+
<div class="row mb-1">
|
124
|
+
|
125
|
+
</div>
|
126
|
+
|
127
|
+
|
61
128
|
{% endblock %}
|
@@ -6,17 +6,19 @@
|
|
6
6
|
<h1 class="row mb-3">Activities Overview & Search</h1>
|
7
7
|
|
8
8
|
<div class="mb-3">
|
9
|
-
{{ search_form(query, equipments_avail, kinds_avail) }}
|
9
|
+
{{ search_form(query, equipments_avail, kinds_avail, search_query_favorites, search_query_last, request_url) }}
|
10
10
|
</div>
|
11
11
|
|
12
12
|
<table class="table table-sort table-arrows">
|
13
13
|
<thead>
|
14
14
|
<tr>
|
15
15
|
<th>Name</th>
|
16
|
-
<th>
|
17
|
-
<th>Kind</th>
|
16
|
+
<th>Date</th>
|
18
17
|
<th class="numeric-sort">Distance</th>
|
19
18
|
<th>Elapsed time</th>
|
19
|
+
<th>Speed / km/h</th>
|
20
|
+
<th>Equipment</th>
|
21
|
+
<th>Kind</th>
|
20
22
|
</tr>
|
21
23
|
</thead>
|
22
24
|
<tbody>
|
@@ -28,9 +30,11 @@
|
|
28
30
|
{{ activity['start']|dt }}
|
29
31
|
{% endif %}
|
30
32
|
</td>
|
31
|
-
<td>{{ activity['kind'] }}</td>
|
32
33
|
<td>{{ '%.1f' % activity["distance_km"] }} km</td>
|
33
34
|
<td>{{ activity.elapsed_time|td }}</td>
|
35
|
+
<td>{{ activity.average_speed_moving_kmh|round(1) }}</td>
|
36
|
+
<td>{{ activity["equipment"] }}</td>
|
37
|
+
<td>{{ activity['kind'] }}</td>
|
34
38
|
</tr>
|
35
39
|
{% endfor %}
|
36
40
|
</tbody>
|
@@ -1,5 +1,4 @@
|
|
1
|
-
{% macro search_form(query, equipments_avail, kinds_avail) %}
|
2
|
-
|
1
|
+
{% macro search_form(query, equipments_avail, kinds_avail, search_query_favorites, search_query_last, request_url) %}
|
3
2
|
<div class="accordion" id="search_form_accordion">
|
4
3
|
<div class="accordion-item">
|
5
4
|
<h2 class="accordion-header">
|
@@ -12,71 +11,106 @@
|
|
12
11
|
<div id="search_form" class="accordion-collapse collapse {{ 'show' if query.active else '' }}"
|
13
12
|
data-bs-parent="#search_form_accordion">
|
14
13
|
<div class="accordion-body">
|
15
|
-
<
|
16
|
-
<div class="
|
17
|
-
<
|
18
|
-
<
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
<input class="form-check-input" type="checkbox" name="name_case_sensitive" value="true"
|
24
|
-
id="name_case_sensitive" {% if query.name_case_sensitive %} checked {% endif %}>
|
25
|
-
<label class="form-check-label" for="name_case_sensitive">
|
26
|
-
Case sensitive
|
27
|
-
</label>
|
28
|
-
</div>
|
29
|
-
</div>
|
14
|
+
<div class="row">
|
15
|
+
<div class="col-8">
|
16
|
+
<form>
|
17
|
+
<div class="row g-3">
|
18
|
+
<div class="col-12">
|
19
|
+
<label for="name" class="form-label">Name</label>
|
20
|
+
<input type="text" class="form-control" id="name" name="name"
|
21
|
+
value="{{ query.name }}">
|
30
22
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
</div>
|
23
|
+
<div class="form-check">
|
24
|
+
<input class="form-check-input" type="checkbox" name="name_case_sensitive"
|
25
|
+
value="true" id="name_case_sensitive" {% if query.name_case_sensitive %}
|
26
|
+
checked {% endif %}>
|
27
|
+
<label class="form-check-label" for="name_case_sensitive">
|
28
|
+
Case sensitive
|
29
|
+
</label>
|
30
|
+
</div>
|
31
|
+
</div>
|
41
32
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
<
|
50
|
-
{{
|
51
|
-
|
33
|
+
<div class="col-6">
|
34
|
+
<label for="start_begin" class="form-label">After</label>
|
35
|
+
<input type="date" class="form-control" id="start_begin" name="start_begin"
|
36
|
+
value="{{ query.start_begin }}">
|
37
|
+
</div>
|
38
|
+
<div class="col-6">
|
39
|
+
<label for="start_end" class="form-label">Until</label>
|
40
|
+
<input type="date" class="form-control" id="start_end" name="start_end"
|
41
|
+
value="{{ query.start_end }}">
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<div class="col-12">
|
45
|
+
<label for="" class="form-label">Kind</label>
|
46
|
+
<div class="form-control">
|
47
|
+
{% for kind in kinds_avail %}
|
48
|
+
<div class="form-check form-check-inline">
|
49
|
+
<input class="form-check-input" type="checkbox" name="kind"
|
50
|
+
value="{{ kind }}" id="kind_{{ kind }}" {% if kind in query.kind %}
|
51
|
+
checked {% endif %}>
|
52
|
+
<label class="form-check-label" for="kind_{{ kind }}">
|
53
|
+
{{ kind }}
|
54
|
+
</label>
|
55
|
+
</div>
|
56
|
+
{% endfor %}
|
57
|
+
</div>
|
58
|
+
</div>
|
59
|
+
|
60
|
+
<div class="col-12">
|
61
|
+
<label for="" class="form-label">Equipment</label>
|
62
|
+
<div class="form-control">
|
63
|
+
{% for equipment in equipments_avail %}
|
64
|
+
<div class="form-check form-check-inline">
|
65
|
+
<input class="form-check-input" type="checkbox" name="equipment"
|
66
|
+
value="{{ equipment }}" id="equipment_{{ equipment }}" {% if equipment
|
67
|
+
in query.equipment %} checked {% endif %}>
|
68
|
+
<label class="form-check-label" for="equipment_{{ equipment }}">
|
69
|
+
{{ equipment }}
|
70
|
+
</label>
|
71
|
+
</div>
|
72
|
+
{% endfor %}
|
73
|
+
</div>
|
52
74
|
</div>
|
53
|
-
{% endfor %}
|
54
|
-
</div>
|
55
|
-
</div>
|
56
75
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
<input class="form-check-input" type="checkbox" name="equipment"
|
63
|
-
value="{{ equipment }}" id="equipment_{{ equipment }}" {% if equipment in
|
64
|
-
query.equipment %} checked {% endif %}>
|
65
|
-
<label class="form-check-label" for="equipment_{{ equipment }}">
|
66
|
-
{{ equipment }}
|
67
|
-
</label>
|
76
|
+
<div class="col-6">
|
77
|
+
<button type="submit" class="btn btn-primary">Filter</button>
|
78
|
+
</div>
|
79
|
+
<div class="col-6">
|
80
|
+
<a class="btn btn-danger" href="?">Reset</a>
|
68
81
|
</div>
|
69
|
-
{% endfor %}
|
70
82
|
</div>
|
71
|
-
</
|
83
|
+
</form>
|
84
|
+
</div>
|
85
|
+
<div class="col-4">
|
86
|
+
<h5 class="mb-3">Favorite queries</h5>
|
87
|
+
<ul class="mb-3">
|
88
|
+
{% for description, url_parameters in search_query_favorites %}
|
89
|
+
{{ _show_search_query(description, url_parameters, True, request_url) }}
|
90
|
+
{% endfor %}
|
91
|
+
</ul>
|
72
92
|
|
73
|
-
<
|
74
|
-
|
75
|
-
|
93
|
+
<h5 class="mb-3">Last queries</h5>
|
94
|
+
<ol class="mb-3">
|
95
|
+
{% for description, url_parameters in search_query_last %}
|
96
|
+
{{ _show_search_query(description, url_parameters, False, request_url) }}
|
97
|
+
{% endfor %}
|
98
|
+
</ol>
|
76
99
|
</div>
|
77
|
-
</
|
100
|
+
</div>
|
78
101
|
</div>
|
79
102
|
</div>
|
80
103
|
</div>
|
81
104
|
</div>
|
105
|
+
{% endmacro %}
|
106
|
+
|
107
|
+
{% macro _show_search_query(description, url_parameters, is_favorite, request_url) %}
|
108
|
+
<li>
|
109
|
+
<a href="?{{ url_parameters }}">{{ description }}</a>
|
110
|
+
{% if is_favorite %}
|
111
|
+
<a href="{{ url_for('search.delete_search_query') }}?{{ url_parameters }}&redirect={{ request_url }}">🗑️</a>
|
112
|
+
{% else %}
|
113
|
+
<a href="{{ url_for('search.save_search_query') }}?{{ url_parameters }}&redirect={{ request_url }}">💾</a>
|
114
|
+
{% endif %}
|
115
|
+
</li>
|
82
116
|
{% endmacro %}
|