mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-22 14:02:24 +01:00
parent
d5492cbb9b
commit
e75f2f92e2
@ -84,4 +84,4 @@ def get_package_tabs(user: User, package: Package):
|
||||
return retval
|
||||
|
||||
|
||||
from . import packages, screenshots, releases, reviews, game_hub
|
||||
from . import packages, advanced_search, screenshots, releases, reviews, game_hub
|
||||
|
92
app/blueprints/packages/advanced_search.py
Normal file
92
app/blueprints/packages/advanced_search.py
Normal file
@ -0,0 +1,92 @@
|
||||
# ContentDB
|
||||
# Copyright (C) 2024 rubenwardy
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from flask import render_template
|
||||
from flask_babel import lazy_gettext, gettext
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms.fields.choices import SelectField, SelectMultipleField
|
||||
from wtforms.fields.simple import StringField, BooleanField
|
||||
from wtforms.validators import Optional
|
||||
from wtforms_sqlalchemy.fields import QuerySelectMultipleField, QuerySelectField
|
||||
|
||||
from . import bp
|
||||
from ...models import PackageType, Tag, db, ContentWarning, License, Language, MinetestRelease
|
||||
|
||||
|
||||
def make_label(obj: Tag | ContentWarning):
|
||||
translated = obj.get_translated()
|
||||
if translated["description"]:
|
||||
return "{}: {}".format(translated["title"], translated["description"])
|
||||
else:
|
||||
return translated["title"]
|
||||
|
||||
|
||||
def get_hide_choices():
|
||||
ret = [
|
||||
("android_default", gettext("Android Default")),
|
||||
("desktop_default", gettext("Desktop Default")),
|
||||
("nonfree", gettext("Non-free")),
|
||||
("wip", gettext("Work in Progress")),
|
||||
("deprecated", gettext("Deprecated")),
|
||||
("*", gettext("All content warnings")),
|
||||
]
|
||||
content_warnings = ContentWarning.query.order_by(db.asc(ContentWarning.name)).all()
|
||||
tags = Tag.query.order_by(db.asc(Tag.name)).all()
|
||||
ret += [(x.name, make_label(x)) for x in content_warnings + tags]
|
||||
return ret
|
||||
|
||||
|
||||
class AdvancedSearchForm(FlaskForm):
|
||||
q = StringField(lazy_gettext("Query"), [Optional()])
|
||||
type = SelectMultipleField(lazy_gettext("Type"), [Optional()], choices=PackageType.choices(),
|
||||
coerce=PackageType.coerce)
|
||||
author = StringField(lazy_gettext("Author"), [Optional()])
|
||||
tag = QuerySelectMultipleField(lazy_gettext('Tags'),
|
||||
query_factory=lambda: Tag.query.order_by(db.asc(Tag.name)),
|
||||
get_pk=lambda a: a.id, get_label=make_label)
|
||||
flag = QuerySelectMultipleField(lazy_gettext('Content Warnings'), query_factory=lambda: ContentWarning.query.order_by(db.asc(ContentWarning.name)), get_pk=lambda a: a.id, get_label=make_label)
|
||||
license = QuerySelectMultipleField(lazy_gettext("License"), [Optional()], allow_blank=True, query_factory=lambda: License.query.order_by(db.asc(License.name)), get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
game = StringField(lazy_gettext("Supports Game"), [Optional()])
|
||||
lang = QuerySelectField(lazy_gettext("Language"), allow_blank=True,
|
||||
query_factory=lambda: Language.query.order_by(db.asc(Language.title)),
|
||||
get_pk=lambda a: a.id, get_label=lambda a: a.title)
|
||||
hide = SelectMultipleField(lazy_gettext("Hide Tags and Content Warnings"), [Optional()])
|
||||
engine_version = QuerySelectField(lazy_gettext("Minetest Version"), allow_blank=True,
|
||||
query_factory=lambda: MinetestRelease.query.order_by(db.asc(MinetestRelease.id)),
|
||||
get_pk=lambda a: a.value, get_label=lambda a: a.name)
|
||||
sort = SelectField(lazy_gettext("Sort by"), [Optional()], choices=[
|
||||
("", ""),
|
||||
("name", lazy_gettext("Name")),
|
||||
("title", lazy_gettext("Title")),
|
||||
("score", lazy_gettext("Package score")),
|
||||
("reviews", lazy_gettext("Reviews")),
|
||||
("downloads", lazy_gettext("Downloads")),
|
||||
("created_at", lazy_gettext("Created At")),
|
||||
("approved_at", lazy_gettext("Approved At")),
|
||||
("last_release", lazy_gettext("Last Release")),
|
||||
])
|
||||
order = SelectField(lazy_gettext("Order"), [Optional()], choices=[
|
||||
("desc", lazy_gettext("Descending")),
|
||||
("asc", lazy_gettext("Ascending")),
|
||||
])
|
||||
random = BooleanField(lazy_gettext("Random order"))
|
||||
|
||||
|
||||
@bp.route("/packages/advanced-search/")
|
||||
def advanced_search():
|
||||
form = AdvancedSearchForm()
|
||||
form.hide.choices = get_hide_choices()
|
||||
return render_template("packages/advanced_search.html", form=form)
|
@ -126,7 +126,7 @@ class PackageType(enum.Enum):
|
||||
|
||||
@classmethod
|
||||
def choices(cls):
|
||||
return [(choice, choice.text) for choice in cls]
|
||||
return [(choice.name.lower(), choice.text) for choice in cls]
|
||||
|
||||
@classmethod
|
||||
def coerce(cls, item):
|
||||
@ -853,7 +853,7 @@ class Package(db.Model):
|
||||
}
|
||||
|
||||
def recalculate_score(self):
|
||||
review_scores = [ 100 * r.as_weight() for r in self.reviews ]
|
||||
review_scores = [ 150 * r.as_weight() for r in self.reviews ]
|
||||
self.score = self.score_downloads + sum(review_scores)
|
||||
|
||||
def get_conf_file_name(self):
|
||||
@ -1042,6 +1042,10 @@ class MinetestRelease(db.Model):
|
||||
self.name = name
|
||||
self.protocol = protocol
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.name
|
||||
|
||||
def get_actual(self):
|
||||
return None if self.name == "None" else self
|
||||
|
||||
|
@ -138,6 +138,8 @@ class QueryBuilder:
|
||||
self.lucky = "lucky" in args
|
||||
self.limit = 1 if self.lucky else get_int_or_abort(args.get("limit"), None)
|
||||
self.order_by = args.get("sort")
|
||||
if self.order_by == "":
|
||||
self.order_by = None
|
||||
self.order_dir = args.get("order") or "desc"
|
||||
|
||||
if "android_default" in self.hide_flags:
|
||||
@ -161,6 +163,9 @@ class QueryBuilder:
|
||||
|
||||
protocol_version = get_int_or_abort(args.get("protocol_version"))
|
||||
minetest_version = args.get("engine_version")
|
||||
if minetest_version == "__None":
|
||||
minetest_version = None
|
||||
|
||||
if protocol_version or minetest_version:
|
||||
self.version = MinetestRelease.get(minetest_version, protocol_version)
|
||||
else:
|
||||
@ -176,8 +181,14 @@ class QueryBuilder:
|
||||
self.game = args.get("game")
|
||||
if self.game:
|
||||
self.game = Package.get_by_key(self.game)
|
||||
if self.game is None:
|
||||
abort(make_response("Unable to find that game"), 400)
|
||||
else:
|
||||
self.game = None
|
||||
|
||||
self.has_lang = args.get("lang")
|
||||
if self.has_lang == "__None":
|
||||
self.has_lang = None
|
||||
|
||||
if cookies and request.cookies.get("hide_nonfree") == "1":
|
||||
self.hide_nonfree = True
|
||||
@ -244,7 +255,7 @@ class QueryBuilder:
|
||||
if self.game:
|
||||
query = query.filter(Package.supported_games.any(game=self.game, supports=True))
|
||||
|
||||
if self.has_lang:
|
||||
if self.has_lang and self.has_lang != "en":
|
||||
query = query.filter(Package.translations.any(language_id=self.has_lang))
|
||||
|
||||
for tag in self.tags:
|
||||
|
@ -253,6 +253,7 @@
|
||||
{% if request.endpoint != "flatpage" and request.endpoint != "report.report" %}
|
||||
<li class="list-inline-item"><a href="{{ url_for('report.report', url=url_current()) }}">{{ _("Report / DMCA") }}</a></li>
|
||||
{% endif %}
|
||||
<li class="list-inline-item"><a href="{{ url_for('packages.advanced_search') }}">{{ _("Advanced Search") }}</a></li>
|
||||
<li class="list-inline-item"><a href="{{ url_for('users.list_all') }}">{{ _("User List") }}</a></li>
|
||||
<li class="list-inline-item"><a href="{{ url_for('threads.list_all') }}">{{ _("Threads") }}</a></li>
|
||||
<li class="list-inline-item"><a href="{{ url_for('collections.list_all') }}">{{ _("Collections") }}</a></li>
|
||||
|
36
app/templates/packages/advanced_search.html
Normal file
36
app/templates/packages/advanced_search.html
Normal file
@ -0,0 +1,36 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{{ _("Advanced Search") }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ self.title() }}</h1>
|
||||
|
||||
{% from "macros/forms.html" import render_field, render_checkbox_field, render_submit_field %}
|
||||
<form method="get" action="{{ url_for('packages.list_all') }}">
|
||||
{{ render_field(form.q) }}
|
||||
{{ render_field(form.type, hint=_("Use shift to select multiple. Leave selection empty to match any type.")) }}
|
||||
{{ render_field(form.license, hint=_("Use shift to select multiple.")) }}
|
||||
{{ render_field(form.lang) }}
|
||||
|
||||
<h2>{{ _("Tags and Content Warnings") }}</h2>
|
||||
|
||||
{{ render_field(form.tag, hint=_("Use shift to select multiple.")) }}
|
||||
{{ render_field(form.flag, hint=_("Use shift to select multiple.")) }}
|
||||
{{ render_field(form.hide, hint=_("Use shift to select multiple.")) }}
|
||||
|
||||
<h2>{{ _("Compatibility") }}</h2>
|
||||
|
||||
{{ render_field(form.engine_version) }}
|
||||
{{ render_field(form.game, placeholder=_("author/name"), pattern="\w+/\w+", hint=_("author/name")) }}
|
||||
|
||||
<h2>{{ _("Sorting") }}</h2>
|
||||
|
||||
{{ render_field(form.sort) }}
|
||||
{{ render_field(form.order) }}
|
||||
{{ render_checkbox_field(form.random) }}
|
||||
|
||||
<button type="submit" class="btn btn-primary mt-5">{{ _("Search") }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
Loading…
Reference in New Issue
Block a user