mirror of
https://github.com/minetest/contentdb.git
synced 2024-11-08 16:43:47 +01:00
Add supports_all_games to make game support explicit
Fixes #388 and fixes #441
This commit is contained in:
parent
cb352fad47
commit
c498818e8b
@ -140,8 +140,6 @@ def remind_wip():
|
||||
packages = [pkg[0] for pkg in packages]
|
||||
packages_list = _package_list(packages)
|
||||
havent = "haven't" if len(packages) > 1 else "hasn't"
|
||||
if len(packages_list) + 54 > 100:
|
||||
packages_list = packages_list[0:(100-54-1)] + "…"
|
||||
|
||||
addNotification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
||||
f"Did you forget? {packages_list} {havent} been submitted for review yet",
|
||||
@ -156,7 +154,7 @@ def remind_outdated():
|
||||
system_user = get_system_user()
|
||||
for user in users:
|
||||
packages = db.session.query(Package.title).filter(
|
||||
Package.maintainers.any(User.id==user.id),
|
||||
Package.maintainers.contains(user),
|
||||
Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \
|
||||
.all()
|
||||
|
||||
@ -234,7 +232,7 @@ def remind_video_url():
|
||||
system_user = get_system_user()
|
||||
for user in users:
|
||||
packages = db.session.query(Package.title).filter(
|
||||
or_(Package.author==user, Package.maintainers.any(User.id==user.id)),
|
||||
or_(Package.author==user, Package.maintainers.contains(user)),
|
||||
Package.video_url==None,
|
||||
Package.type == PackageType.GAME,
|
||||
Package.state == PackageState.APPROVED) \
|
||||
@ -250,6 +248,35 @@ def remind_video_url():
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@action("Send missing game support notifications")
|
||||
def remind_missing_game_support():
|
||||
users = User.query.filter(
|
||||
User.maintained_packages.any(and_(
|
||||
Package.state != PackageState.DELETED,
|
||||
Package.type.in_([PackageType.MOD, PackageType.TXP]),
|
||||
~Package.supported_games.any(),
|
||||
Package.supports_all_games == False))).all()
|
||||
|
||||
system_user = get_system_user()
|
||||
for user in users:
|
||||
packages = db.session.query(Package.title).filter(
|
||||
Package.maintainers.contains(user),
|
||||
Package.state != PackageState.DELETED,
|
||||
Package.type.in_([PackageType.MOD, PackageType.TXP]),
|
||||
~Package.supported_games.any(),
|
||||
Package.supports_all_games == False) \
|
||||
.all()
|
||||
|
||||
packages = [pkg[0] for pkg in packages]
|
||||
packages_list = _package_list(packages)
|
||||
|
||||
addNotification(user, system_user, NotificationType.PACKAGE_APPROVAL,
|
||||
f"You need to confirm whether the following packages support all games: {packages_list}",
|
||||
url_for('todo.all_game_support', username=user.username))
|
||||
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@action("Detect game support")
|
||||
def detect_game_support():
|
||||
task_id = uuid()
|
||||
|
@ -230,7 +230,7 @@ def list_all_releases():
|
||||
if maintainer is None:
|
||||
error(404, "Maintainer not found")
|
||||
query = query.join(Package)
|
||||
query = query.filter(Package.maintainers.any(id=maintainer.id))
|
||||
query = query.filter(Package.maintainers.contains(maintainer))
|
||||
|
||||
return jsonify([ rel.getLongAsDictionary() for rel in query.limit(30).all() ])
|
||||
|
||||
|
@ -644,6 +644,7 @@ class GameSupportForm(FlaskForm):
|
||||
enable_support_detection = BooleanField(lazy_gettext("Enable support detection based on dependencies (recommended)"), [Optional()])
|
||||
supported = StringField(lazy_gettext("Supported games (Comma-separated)"), [Optional()])
|
||||
unsupported = StringField(lazy_gettext("Unsupported games (Comma-separated)"), [Optional()])
|
||||
supports_all_games = BooleanField(lazy_gettext("Supports all games (unless stated)"), [Optional()])
|
||||
submit = SubmitField(lazy_gettext("Save"))
|
||||
|
||||
|
||||
@ -661,17 +662,25 @@ def game_support(package):
|
||||
force_game_detection = package.supported_games.filter(and_(
|
||||
PackageGameSupport.confidence > 1, PackageGameSupport.supports == True)).count() == 0
|
||||
|
||||
can_override = can_edit and current_user not in package.maintainers
|
||||
|
||||
form = GameSupportForm() if can_edit else None
|
||||
if form and request.method == "GET":
|
||||
form.enable_support_detection.data = package.enable_game_support_detection
|
||||
form.supports_all_games.data = package.supports_all_games
|
||||
|
||||
if can_override:
|
||||
manual_supported_games = package.supported_games.filter_by(confidence=11).all()
|
||||
form.supported.data = ", ".join([x.game.name for x in manual_supported_games if x.supports])
|
||||
form.unsupported.data = ", ".join([x.game.name for x in manual_supported_games if not x.supports])
|
||||
else:
|
||||
form.supported = None
|
||||
form.unsupported = None
|
||||
|
||||
if form and form.validate_on_submit():
|
||||
detect_update_needed = False
|
||||
|
||||
if current_user not in package.maintainers:
|
||||
if can_override:
|
||||
try:
|
||||
resolver = GameSupportResolver(db.session)
|
||||
|
||||
@ -695,6 +704,8 @@ def game_support(package):
|
||||
else:
|
||||
package.supported_games.filter_by(confidence=1).delete()
|
||||
|
||||
package.supports_all_games = form.supports_all_games.data
|
||||
|
||||
db.session.commit()
|
||||
|
||||
if detect_update_needed:
|
||||
@ -708,7 +719,10 @@ def game_support(package):
|
||||
|
||||
all_game_support = package.supported_games.all()
|
||||
all_game_support.sort(key=lambda x: -x.game.score)
|
||||
supported_games = ", ".join([x.game.name for x in all_game_support if x.supports])
|
||||
supported_games_list: List[str] = [x.game.name for x in all_game_support if x.supports]
|
||||
if package.supports_all_games:
|
||||
supported_games_list.insert(0, "*")
|
||||
supported_games = ", ".join(supported_games_list)
|
||||
unsupported_games = ", ".join([x.game.name for x in all_game_support if not x.supports])
|
||||
|
||||
mod_conf_lines = ""
|
||||
|
@ -15,7 +15,8 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
from celery import uuid
|
||||
from flask import redirect, url_for, abort, render_template
|
||||
from flask import redirect, url_for, abort, render_template, flash
|
||||
from flask_babel import gettext
|
||||
from flask_login import current_user, login_required
|
||||
from sqlalchemy import or_, and_
|
||||
|
||||
@ -23,7 +24,6 @@ from app.models import User, Package, PackageState, PackageScreenshot, PackageUp
|
||||
PackageRelease, Permission, NotificationType, AuditSeverity, UserRank, PackageType
|
||||
from app.tasks.importtasks import makeVCSRelease
|
||||
from app.utils import addNotification, addAuditLog
|
||||
|
||||
from . import bp
|
||||
|
||||
|
||||
@ -59,7 +59,8 @@ def view_user(username=None):
|
||||
missing_game_support = user.maintained_packages.filter(
|
||||
Package.state != PackageState.DELETED,
|
||||
Package.type.in_([PackageType.MOD, PackageType.TXP]),
|
||||
~Package.supported_games.any()) \
|
||||
~Package.supported_games.any(),
|
||||
Package.supports_all_games == False) \
|
||||
.order_by(db.asc(Package.title)).all()
|
||||
|
||||
packages_with_no_screenshots = user.maintained_packages.filter(
|
||||
@ -152,3 +153,22 @@ def all_game_support(username=None):
|
||||
.order_by(db.asc(Package.title)).all()
|
||||
|
||||
return render_template("todo/game_support.html", user=user, packages=packages)
|
||||
|
||||
|
||||
@bp.route("/users/<username>/confirm_supports_all_games/", methods=["POST"])
|
||||
@login_required
|
||||
def confirm_supports_all_games(username=None):
|
||||
user: User = User.query.filter_by(username=username).one_or_404()
|
||||
if current_user != user and not current_user.rank.atLeast(UserRank.EDITOR):
|
||||
abort(403)
|
||||
|
||||
db.session.query(Package).filter(
|
||||
Package.maintainers.contains(user),
|
||||
Package.state != PackageState.DELETED,
|
||||
Package.type.in_([PackageType.MOD, PackageType.TXP]),
|
||||
~Package.supported_games.any(supports=True)).update({ "supports_all_games": True })
|
||||
|
||||
db.session.commit()
|
||||
|
||||
flash(gettext("Done"), "success")
|
||||
return redirect(url_for("todo.all_game_support", username=current_user.username))
|
||||
|
@ -12,7 +12,7 @@ user experience.
|
||||
|
||||
## Support sources
|
||||
|
||||
### mod.conf
|
||||
### mod.conf / texture_pack.conf
|
||||
|
||||
You can use `supported_games` to specify games that your mod is compatible with.
|
||||
|
||||
@ -24,6 +24,16 @@ Both of these are comma-separated lists of game technical ids. Any `_game` suffi
|
||||
supported_games = minetest_game, repixture
|
||||
unsupported_games = lordofthetest, nodecore, whynot
|
||||
|
||||
If your package supports all games by default, you can put "*` in supported_games.
|
||||
You can still use unsupported_games to mark games as unsupported.
|
||||
You can also specify games that you've tested in supported_games.
|
||||
|
||||
# Should work with all games but I've only tested using Minetest Game:
|
||||
supported_games = *, minetest_games
|
||||
|
||||
# But doesn't work in capturetheflag
|
||||
unsupported_game = capturetheflag
|
||||
|
||||
### Dependencies
|
||||
|
||||
ContentDB will analyse hard dependencies and work out which games a mod supports.
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
import datetime
|
||||
import enum
|
||||
import typing
|
||||
|
||||
from flask import url_for
|
||||
from flask_babel import lazy_gettext
|
||||
@ -410,6 +411,9 @@ class Package(db.Model):
|
||||
review_thread = db.relationship("Thread", uselist=False, foreign_keys=[review_thread_id],
|
||||
back_populates="is_review_thread", post_update=True)
|
||||
|
||||
# Supports all games by default, may have unsupported games
|
||||
supports_all_games = db.Column(db.Boolean, nullable=False, default=False)
|
||||
|
||||
# Downloads
|
||||
repo = db.Column(db.String(200), nullable=True)
|
||||
website = db.Column(db.String(200), nullable=True)
|
||||
@ -520,15 +524,24 @@ class Package(db.Model):
|
||||
def getSortedOptionalDependencies(self):
|
||||
return self.getSortedDependencies(False)
|
||||
|
||||
def getSortedSupportedGames(self, include_unsupported=False):
|
||||
query = self.supported_games
|
||||
if not include_unsupported:
|
||||
query = query.filter(PackageGameSupport.game.has(state=PackageState.APPROVED)).filter_by(supports=True)
|
||||
def get_sorted_game_support(self):
|
||||
query = self.supported_games.filter(PackageGameSupport.game.has(state=PackageState.APPROVED))
|
||||
|
||||
supported = query.all()
|
||||
supported.sort(key=lambda x: -(x.game.score + 100000*x.confidence))
|
||||
return supported
|
||||
|
||||
def get_sorted_game_support_pair(self):
|
||||
supported = self.get_sorted_game_support()
|
||||
return [
|
||||
[x for x in supported if x.supports],
|
||||
[x for x in supported if not x.supports],
|
||||
]
|
||||
|
||||
def has_game_support_confirmed(self):
|
||||
return self.supports_all_games or \
|
||||
self.supported_games.filter(PackageGameSupport.confidence > 1).count() > 0
|
||||
|
||||
def getAsDictionaryKey(self):
|
||||
return {
|
||||
"name": self.name,
|
||||
|
@ -80,7 +80,7 @@ def getMeta(urlstr, author):
|
||||
@celery.task()
|
||||
def updateAllGameSupport():
|
||||
resolver = GameSupportResolver(db.session)
|
||||
resolver.update_all()
|
||||
resolver.init_all()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@ -153,6 +153,10 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
||||
if "supported_games" in tree.meta:
|
||||
for game in get_games_from_csv(db.session, tree.meta["supported_games"]):
|
||||
game_is_supported[game.id] = True
|
||||
|
||||
has_star = any(map(lambda x: x.strip() == "*", tree.meta["supported_games"].split(",")))
|
||||
if has_star:
|
||||
package.supports_all_games = True
|
||||
if "unsupported_games" in tree.meta:
|
||||
for game in get_games_from_csv(db.session, tree.meta["unsupported_games"]):
|
||||
game_is_supported[game.id] = False
|
||||
|
@ -9,9 +9,8 @@
|
||||
{{ _("Documentation") }}
|
||||
</a>
|
||||
<h2 class="mt-0">{{ self.title() }}</h2>
|
||||
|
||||
<p class="alert alert-warning">
|
||||
This feature is experimental
|
||||
<p>
|
||||
{{ _("Game support is configured using the package's .conf file. See the documentation for more info") }}
|
||||
</p>
|
||||
|
||||
<div class="list-group">
|
||||
@ -29,7 +28,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for support in package.getSortedSupportedGames(True) %}
|
||||
{% if package.supports_all_games %}
|
||||
<div class="list-group-item">
|
||||
{{ _("Supports all games unless otherwise stated") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% for support in package.get_sorted_game_support() %}
|
||||
<a class="list-group-item list-group-item-action"
|
||||
href="{{ support.game.getURL('packages.view') }}">
|
||||
<div class="row">
|
||||
@ -83,7 +88,13 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if package.checkPerm(current_user, "EDIT_PACKAGE") and current_user not in package.maintainers %}
|
||||
{{ render_checkbox_field(form.supports_all_games) }}
|
||||
<p class="text-muted">
|
||||
{{ _("Unless otherwise stated, this package should work with all games.") }}
|
||||
{{ _("You can check this and still specify games in supported_games that you've tested.") }}
|
||||
</p>
|
||||
|
||||
{% if form.supported and form.unsupported %}
|
||||
<h3>
|
||||
{{ _("Editor Overrides") }}
|
||||
<i class="ml-2 fas fa-user-edit"></i>
|
||||
|
@ -445,8 +445,11 @@
|
||||
{% endif %}
|
||||
|
||||
{% if package.type == package.type.MOD or package.type == package.type.TXP %}
|
||||
{% set supported_games = package.getSortedSupportedGames() %}
|
||||
{% if supported_games or package.type == package.type.MOD %}
|
||||
{% set pair = package.get_sorted_game_support_pair() %}
|
||||
{% set supported_games = pair[0] %}
|
||||
{% set unsupported_games = pair[1] %}
|
||||
{% set show_unsupported = package.supports_all_games or supported_games == [] %}
|
||||
{% if supported_games or unsupported_games or package.type == package.type.MOD %}
|
||||
<h3>
|
||||
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %}
|
||||
<a href="{{ package.getURL('packages.game_support') }}" class="btn btn-secondary btn-sm float-right">
|
||||
@ -455,19 +458,63 @@
|
||||
{% endif %}
|
||||
{{ _("Compatible Games") }}
|
||||
</h3>
|
||||
<div style="max-height: 300px; overflow: hidden auto;">
|
||||
|
||||
{% if package.supports_all_games %}
|
||||
<p>
|
||||
{{ _("Should support most games.") }}
|
||||
{% if supported_games %}
|
||||
<br>
|
||||
{{ _("Tested with:") }}
|
||||
{% endif %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if supported_games %}
|
||||
<div style="max-height: 300px; overflow: hidden auto;" class="mb-3">
|
||||
{% for support in supported_games %}
|
||||
<a class="badge badge-secondary"
|
||||
href="{{ support.game.getURL('packages.view') }}">
|
||||
{{ _("%(title)s by %(display_name)s",
|
||||
title=support.game.title, display_name=support.game.author.display_name) }}
|
||||
</a>
|
||||
{% else %}
|
||||
{{ _("No specific game is required") }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% elif not package.supports_all_games %}
|
||||
<p>
|
||||
{{ _("No specific game required") }}
|
||||
</p>
|
||||
{% if package.checkPerm(current_user, "EDIT_PACKAGE") %}
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
{{ _("Is the above correct?") }}
|
||||
{{ _("You need to either confirm this or tell ContentDB about supported games") }}
|
||||
</p>
|
||||
|
||||
{% if package.type == package.type.MOD %}
|
||||
<a class="btn btn-sm btn-warning" href="{{ package.getURL('packages.game_support') }}">
|
||||
Update
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if unsupported_games and show_unsupported %}
|
||||
<p>
|
||||
{{ _("Except for:") }}
|
||||
</p>
|
||||
<div style="max-height: 300px; overflow: hidden auto;">
|
||||
{% for support in unsupported_games %}
|
||||
<a class="badge badge-danger"
|
||||
href="{{ support.game.getURL('packages.view') }}">
|
||||
<i class="fas fa-times mr-1"></i>
|
||||
{{ _("%(title)s by %(display_name)s",
|
||||
title=support.game.title, display_name=support.game.author.display_name) }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if package.type == package.type.MOD and (supported_games or unsupported_games) and
|
||||
not package.has_game_support_confirmed() %}
|
||||
<p class="text-muted small mt-2 mb-0">
|
||||
{{ _("This is an experimental feature.") }}
|
||||
{{ _("Supported games are determined by an algorithm, and may not be correct.") }}
|
||||
|
@ -10,6 +10,7 @@
|
||||
<p>
|
||||
{{ _("You should specify the games supported by your mods and texture packs.") }}
|
||||
{{ _("Specifying game support makes it easier for players to find your content.") }}
|
||||
{{ _("If your package supports all games unless otherwise stated, confirm this using 'Supports all games'") }}
|
||||
</p>
|
||||
|
||||
|
||||
@ -28,7 +29,15 @@
|
||||
</span>
|
||||
</div>
|
||||
<div class="col">
|
||||
{% set supported_games = package.getSortedSupportedGames() %}
|
||||
{% if package.supports_all_games %}
|
||||
<span class="text-muted pr-2">
|
||||
<i>
|
||||
{{ _("Supports all games") }}
|
||||
</i>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% set supported_games = package.get_sorted_game_support_pair()[0] %}
|
||||
{% if supported_games %}
|
||||
{% for support in supported_games %}
|
||||
<a class="badge badge-secondary"
|
||||
@ -37,11 +46,9 @@
|
||||
title=support.game.title, display_name=support.game.author.display_name) }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<span class="text-muted">
|
||||
<i>
|
||||
{{ _("None listed, assumed to support all games") }}
|
||||
</i>
|
||||
{% elif not package.supports_all_games %}
|
||||
<span class="text-danger">
|
||||
{{ _("No supported games listed. Please either add supported games or check 'Supports all games'") }}
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -55,4 +62,13 @@
|
||||
<p class="text-muted">{{ _("Nothing to do :)") }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<h2>Bulk support all games</h2>
|
||||
<p>
|
||||
{{ _("Click the button below to confirm that all games without listed supported_games (red text above) do support all games, except for any games listed in unsupported_games.") }}
|
||||
</p>
|
||||
<form method="post" action="{{ url_for('todo.confirm_supports_all_games', username=user.username) }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="submit" value="{{ _('Confirm') }}" class="btn btn-primary">
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
26
migrations/versions/2ecff2f9972d_.py
Normal file
26
migrations/versions/2ecff2f9972d_.py
Normal file
@ -0,0 +1,26 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 2ecff2f9972d
|
||||
Revises: 23afcf580aae
|
||||
Create Date: 2023-06-18 07:51:42.581955
|
||||
|
||||
"""
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2ecff2f9972d'
|
||||
down_revision = '23afcf580aae'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
with op.batch_alter_table('package', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('supports_all_games', sa.Boolean(), nullable=False, server_default="false"))
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.batch_alter_table('package', schema=None) as batch_op:
|
||||
batch_op.drop_column('supports_all_games')
|
Loading…
Reference in New Issue
Block a user