mirror of
https://github.com/minetest/contentdb.git
synced 2024-11-08 16:43:47 +01:00
Add option to disable game support detection
This commit is contained in:
parent
42841896d1
commit
d9e65f7c3a
@ -328,7 +328,7 @@ def update_screenshot_sizes():
|
|||||||
|
|
||||||
@action("Detect game support")
|
@action("Detect game support")
|
||||||
def detect_game_support():
|
def detect_game_support():
|
||||||
resolver = GameSupportResolver()
|
resolver = GameSupportResolver(db.session)
|
||||||
resolver.update_all()
|
resolver.update_all()
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
@ -17,11 +17,11 @@ import typing
|
|||||||
from urllib.parse import quote as urlescape
|
from urllib.parse import quote as urlescape
|
||||||
|
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from flask_babel import lazy_gettext, gettext
|
from celery import uuid
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
from jinja2 import Markup
|
from jinja2 import Markup
|
||||||
from sqlalchemy import or_, func, and_
|
from sqlalchemy import func
|
||||||
from sqlalchemy.orm import joinedload, subqueryload
|
from sqlalchemy.orm import joinedload, subqueryload
|
||||||
from wtforms import *
|
from wtforms import *
|
||||||
from wtforms_sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField
|
from wtforms_sqlalchemy.fields import QuerySelectField, QuerySelectMultipleField
|
||||||
@ -29,7 +29,7 @@ from wtforms.validators import *
|
|||||||
|
|
||||||
from app.querybuilder import QueryBuilder
|
from app.querybuilder import QueryBuilder
|
||||||
from app.rediscache import has_key, set_key
|
from app.rediscache import has_key, set_key
|
||||||
from app.tasks.importtasks import importRepoScreenshot
|
from app.tasks.importtasks import importRepoScreenshot, checkZipRelease
|
||||||
from app.utils import *
|
from app.utils import *
|
||||||
from . import bp, get_package_tabs
|
from . import bp, get_package_tabs
|
||||||
from app.logic.LogicError import LogicError
|
from app.logic.LogicError import LogicError
|
||||||
@ -628,6 +628,7 @@ def similar(package):
|
|||||||
|
|
||||||
|
|
||||||
class GameSupportForm(FlaskForm):
|
class GameSupportForm(FlaskForm):
|
||||||
|
enable_support_detection = BooleanField(lazy_gettext("Enable support detection based on dependencies"))
|
||||||
supported = StringField(lazy_gettext("Supported games (Comma-separated)"), [Optional()])
|
supported = StringField(lazy_gettext("Supported games (Comma-separated)"), [Optional()])
|
||||||
unsupported = StringField(lazy_gettext("Unsupported games (Comma-separated)"), [Optional()])
|
unsupported = StringField(lazy_gettext("Unsupported games (Comma-separated)"), [Optional()])
|
||||||
submit = SubmitField(lazy_gettext("Save"))
|
submit = SubmitField(lazy_gettext("Save"))
|
||||||
@ -646,21 +647,38 @@ def game_support(package):
|
|||||||
|
|
||||||
form = GameSupportForm() if can_edit else None
|
form = GameSupportForm() if can_edit else None
|
||||||
if request.method == "GET":
|
if request.method == "GET":
|
||||||
|
form.enable_support_detection.data = package.enable_game_support_detection
|
||||||
manual_supported_games = package.supported_games.filter_by(confidence=8).all()
|
manual_supported_games = package.supported_games.filter_by(confidence=8).all()
|
||||||
form.supported.data = ", ".join([x.game.name for x in manual_supported_games if x.supports])
|
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])
|
form.unsupported.data = ", ".join([x.game.name for x in manual_supported_games if not x.supports])
|
||||||
|
|
||||||
if form and form.validate_on_submit():
|
if form and form.validate_on_submit():
|
||||||
resolver = GameSupportResolver()
|
resolver = GameSupportResolver(db.session)
|
||||||
game_is_supported = []
|
|
||||||
for game in get_games_from_csv(form.supported.data or ""):
|
|
||||||
game_is_supported.append((game, True))
|
|
||||||
for game in get_games_from_csv(form.unsupported.data or ""):
|
|
||||||
game_is_supported.append((game, False))
|
|
||||||
resolver.set_supported(package, game_is_supported, 8)
|
|
||||||
db.session.commit()
|
|
||||||
|
|
||||||
return redirect(package.getURL("packages.game_support"))
|
game_is_supported = {}
|
||||||
|
for game in get_games_from_csv(db.session, form.supported.data or ""):
|
||||||
|
game_is_supported[game.id] = True
|
||||||
|
for game in get_games_from_csv(db.session, form.unsupported.data or ""):
|
||||||
|
game_is_supported[game.id] = False
|
||||||
|
resolver.set_supported(package, game_is_supported, 8)
|
||||||
|
|
||||||
|
next_url = package.getURL("packages.game_support")
|
||||||
|
|
||||||
|
if form.enable_support_detection.data != package.enable_game_support_detection:
|
||||||
|
package.enable_game_support_detection = form.enable_support_detection.data
|
||||||
|
if package.enable_game_support_detection:
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
release = package.releases.first()
|
||||||
|
if release:
|
||||||
|
task_id = uuid()
|
||||||
|
checkZipRelease.apply_async((release.id, release.file_path), task_id=task_id)
|
||||||
|
next_url = url_for("tasks.check", id=task_id, r=next_url)
|
||||||
|
else:
|
||||||
|
package.supported_games.filter_by(confidence=1).delete()
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return redirect(next_url)
|
||||||
|
|
||||||
return render_template("packages/game_support.html", package=package, form=form,
|
return render_template("packages/game_support.html", package=package, form=form,
|
||||||
tabs=get_package_tabs(current_user, package), current_tab="game_support")
|
tabs=get_package_tabs(current_user, package), current_tab="game_support")
|
||||||
|
@ -16,11 +16,12 @@
|
|||||||
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
from typing import List, Dict, Optional, Iterable
|
||||||
|
|
||||||
from typing import List, Dict, Optional, Iterator, Iterable, Tuple
|
import sqlalchemy.orm
|
||||||
|
|
||||||
from app.logic.LogicError import LogicError
|
from app.logic.LogicError import LogicError
|
||||||
from app.models import Package, MetaPackage, PackageType, PackageState, PackageGameSupport, db
|
from app.models import Package, MetaPackage, PackageType, PackageState, PackageGameSupport
|
||||||
|
|
||||||
"""
|
"""
|
||||||
get_game_support(package):
|
get_game_support(package):
|
||||||
@ -82,11 +83,15 @@ class PackageSet:
|
|||||||
|
|
||||||
|
|
||||||
class GameSupportResolver:
|
class GameSupportResolver:
|
||||||
|
session: sqlalchemy.orm.Session
|
||||||
checked_packages = set()
|
checked_packages = set()
|
||||||
checked_metapackages = set()
|
checked_metapackages = set()
|
||||||
resolved_packages: Dict[str, PackageSet] = {}
|
resolved_packages: Dict[str, PackageSet] = {}
|
||||||
resolved_metapackages: Dict[str, PackageSet] = {}
|
resolved_metapackages: Dict[str, PackageSet] = {}
|
||||||
|
|
||||||
|
def __init__(self, session):
|
||||||
|
self.session = session
|
||||||
|
|
||||||
def resolve_for_meta_package(self, meta: MetaPackage, history: List[str]) -> PackageSet:
|
def resolve_for_meta_package(self, meta: MetaPackage, history: List[str]) -> PackageSet:
|
||||||
print(f"Resolving for {meta.name}", file=sys.stderr)
|
print(f"Resolving for {meta.name}", file=sys.stderr)
|
||||||
|
|
||||||
@ -120,8 +125,6 @@ class GameSupportResolver:
|
|||||||
return retval
|
return retval
|
||||||
|
|
||||||
def resolve(self, package: Package, history: List[str]) -> PackageSet:
|
def resolve(self, package: Package, history: List[str]) -> PackageSet:
|
||||||
db.session.merge(package)
|
|
||||||
|
|
||||||
key = package.getId()
|
key = package.getId()
|
||||||
print(f"Resolving for {key}", file=sys.stderr)
|
print(f"Resolving for {key}", file=sys.stderr)
|
||||||
|
|
||||||
@ -160,47 +163,41 @@ class GameSupportResolver:
|
|||||||
return retval
|
return retval
|
||||||
|
|
||||||
def update_all(self) -> None:
|
def update_all(self) -> None:
|
||||||
for package in Package.query.filter(Package.type == PackageType.MOD, Package.state != PackageState.DELETED).all():
|
for package in self.session.query(Package).filter(Package.type == PackageType.MOD, Package.state != PackageState.DELETED).all():
|
||||||
retval = self.resolve(package, [])
|
retval = self.resolve(package, [])
|
||||||
for game in retval:
|
for game in retval:
|
||||||
support = PackageGameSupport(package, game, 1, True)
|
support = PackageGameSupport(package, game, 1, True)
|
||||||
db.session.add(support)
|
self.session.add(support)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Update game supported package on a package, given the confidence.
|
Update game supported package on a package, given the confidence.
|
||||||
|
|
||||||
Higher confidences outweigh lower ones.
|
Higher confidences outweigh lower ones.
|
||||||
"""
|
"""
|
||||||
def set_supported(self, package: Package, game_is_supported: List[Tuple[Package, bool]], confidence: int):
|
def set_supported(self, package: Package, game_is_supported: Dict[int, bool], confidence: int):
|
||||||
previous_supported: Dict[int, PackageGameSupport] = {}
|
previous_supported: Dict[int, PackageGameSupport] = {}
|
||||||
for support in package.supported_games.all():
|
for support in package.supported_games.all():
|
||||||
db.session.merge(support.game)
|
|
||||||
previous_supported[support.game.id] = support
|
previous_supported[support.game.id] = support
|
||||||
|
|
||||||
seen_game = {}
|
for game_id, supports in game_is_supported.items():
|
||||||
for game, supports in game_is_supported:
|
game = self.session.query(Package).get(game_id)
|
||||||
if seen_game.get(game.id):
|
lookup = previous_supported.pop(game_id, None)
|
||||||
continue
|
|
||||||
|
|
||||||
seen_game[game.id] = True
|
|
||||||
lookup = previous_supported.pop(game.id, None)
|
|
||||||
if lookup is None:
|
if lookup is None:
|
||||||
support = PackageGameSupport(package, game, confidence, supports)
|
support = PackageGameSupport(package, game, confidence, supports)
|
||||||
db.session.add(support)
|
self.session.add(support)
|
||||||
elif lookup.confidence <= confidence:
|
elif lookup.confidence <= confidence:
|
||||||
lookup.supports = supports
|
lookup.supports = supports
|
||||||
lookup.confidence = confidence
|
lookup.confidence = confidence
|
||||||
db.session.merge(lookup)
|
|
||||||
|
|
||||||
for game, support in previous_supported.items():
|
for game, support in previous_supported.items():
|
||||||
if support.confidence == confidence:
|
if support.confidence == confidence:
|
||||||
db.session.delete(support)
|
self.session.delete(support)
|
||||||
|
|
||||||
def update(self, package: Package) -> None:
|
def update(self, package: Package) -> None:
|
||||||
retval = self.resolve(package, [])
|
game_is_supported = {}
|
||||||
|
if package.enable_game_support_detection:
|
||||||
game_is_supported = []
|
retval = self.resolve(package, [])
|
||||||
for game in retval:
|
for game in retval:
|
||||||
game_is_supported.append((game, True))
|
game_is_supported[game.id] = True
|
||||||
|
|
||||||
self.set_supported(package, game_is_supported, 1)
|
self.set_supported(package, game_is_supported, 1)
|
||||||
|
@ -414,6 +414,8 @@ class Package(db.Model):
|
|||||||
forums = db.Column(db.Integer, nullable=True)
|
forums = db.Column(db.Integer, nullable=True)
|
||||||
video_url = db.Column(db.String(200), nullable=True, default=None)
|
video_url = db.Column(db.String(200), nullable=True, default=None)
|
||||||
|
|
||||||
|
enable_game_support_detection = db.Column(db.Boolean, nullable=False, default=True)
|
||||||
|
|
||||||
provides = db.relationship("MetaPackage", secondary=PackageProvides, order_by=db.asc("name"), back_populates="packages")
|
provides = db.relationship("MetaPackage", secondary=PackageProvides, order_by=db.asc("name"), back_populates="packages")
|
||||||
|
|
||||||
dependencies = db.relationship("Dependency", back_populates="depender", lazy="dynamic", foreign_keys=[Dependency.depender_id])
|
dependencies = db.relationship("Dependency", back_populates="depender", lazy="dynamic", foreign_keys=[Dependency.depender_id])
|
||||||
|
@ -14,9 +14,12 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import gitdb
|
||||||
import json
|
import json
|
||||||
import os, shutil, gitdb
|
import os
|
||||||
|
import shutil
|
||||||
from zipfile import ZipFile
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from git import GitCommandError
|
from git import GitCommandError
|
||||||
from git_archive_all import GitArchiver
|
from git_archive_all import GitArchiver
|
||||||
from kombu import uuid
|
from kombu import uuid
|
||||||
@ -27,7 +30,7 @@ from app.utils import randomString, post_bot_message, addSystemNotification, add
|
|||||||
from app.utils.git import clone_repo, get_latest_tag, get_latest_commit, get_temp_dir
|
from app.utils.git import clone_repo, get_latest_tag, get_latest_commit, get_temp_dir
|
||||||
from .minetestcheck import build_tree, MinetestCheckError, ContentType
|
from .minetestcheck import build_tree, MinetestCheckError, ContentType
|
||||||
from ..logic.LogicError import LogicError
|
from ..logic.LogicError import LogicError
|
||||||
from ..logic.game_support import GameSupportResolver, PackageSet
|
from ..logic.game_support import GameSupportResolver
|
||||||
from ..logic.packages import do_edit_package, ALIASES
|
from ..logic.packages import do_edit_package, ALIASES
|
||||||
from ..utils.image import get_image_size
|
from ..utils.image import get_image_size
|
||||||
|
|
||||||
@ -73,6 +76,25 @@ def getMeta(urlstr, author):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@celery.task()
|
||||||
|
def releaseUpdateGameSupport(package_id: int, supported_games, unsupported_games):
|
||||||
|
with db.create_session({})() as session:
|
||||||
|
package = session.query(Package).get(package_id)
|
||||||
|
resolver = GameSupportResolver(session)
|
||||||
|
|
||||||
|
game_is_supported = {}
|
||||||
|
if supported_games:
|
||||||
|
for game in get_games_from_csv(session, supported_games):
|
||||||
|
game_is_supported[game.id] = True
|
||||||
|
if unsupported_games:
|
||||||
|
for game in get_games_from_csv(session, unsupported_games):
|
||||||
|
game_is_supported[game.id] = False
|
||||||
|
|
||||||
|
resolver.set_supported(package, game_is_supported, 10)
|
||||||
|
resolver.update(package)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
|
||||||
def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
||||||
try:
|
try:
|
||||||
tree = build_tree(path, expected_type=ContentType[release.package.type.name],
|
tree = build_tree(path, expected_type=ContentType[release.package.type.name],
|
||||||
@ -115,20 +137,6 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
|||||||
for meta in getMetaPackages(optional_depends):
|
for meta in getMetaPackages(optional_depends):
|
||||||
db.session.add(Dependency(package, meta=meta, optional=True))
|
db.session.add(Dependency(package, meta=meta, optional=True))
|
||||||
|
|
||||||
# Update game supports
|
|
||||||
if package.type == PackageType.MOD:
|
|
||||||
resolver = GameSupportResolver()
|
|
||||||
game_is_supported = []
|
|
||||||
if "supported_games" in tree.meta:
|
|
||||||
for game in get_games_from_csv(tree.meta["supported_games"]):
|
|
||||||
game_is_supported.append((game, True))
|
|
||||||
if "unsupported_games" in tree.meta:
|
|
||||||
for game in get_games_from_csv(tree.meta["unsupported_games"]):
|
|
||||||
game_is_supported.append((game, False))
|
|
||||||
|
|
||||||
resolver.set_supported(package, game_is_supported, 10)
|
|
||||||
resolver.update(package)
|
|
||||||
|
|
||||||
# Update min/max
|
# Update min/max
|
||||||
if tree.meta.get("min_minetest_version"):
|
if tree.meta.get("min_minetest_version"):
|
||||||
release.min_rel = MinetestRelease.get(tree.meta["min_minetest_version"], None)
|
release.min_rel = MinetestRelease.get(tree.meta["min_minetest_version"], None)
|
||||||
@ -145,6 +153,10 @@ def postReleaseCheckUpdate(self, release: PackageRelease, path):
|
|||||||
except IOError:
|
except IOError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Update game support
|
||||||
|
if package.type == PackageType.MOD:
|
||||||
|
releaseUpdateGameSupport.delay(package.id, tree.meta.get("supported_games"), tree.meta.get("unsupported_games"))
|
||||||
|
|
||||||
return tree
|
return tree
|
||||||
|
|
||||||
except MinetestCheckError as err:
|
except MinetestCheckError as err:
|
||||||
|
@ -68,18 +68,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% if form and package.checkPerm(current_user, "EDIT_PACKAGE") and current_user not in package.maintainers %}
|
{% if form %}
|
||||||
<h2>
|
<h2>Options</h2>
|
||||||
{{ _("Added by Editor") }}
|
|
||||||
<i class="ml-2 fas fa-user-edit"></i>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
{% from "macros/forms.html" import render_field, render_checkbox_field, render_submit_field %}
|
{% from "macros/forms.html" import render_field, render_checkbox_field, render_submit_field %}
|
||||||
<form method="POST" action="" class="tableform">
|
<form method="POST" action="" class="tableform">
|
||||||
{{ form.hidden_tag() }}
|
{{ form.hidden_tag() }}
|
||||||
|
|
||||||
{{ render_field(form.supported) }}
|
{{ render_checkbox_field(form.enable_support_detection) }}
|
||||||
{{ render_field(form.unsupported) }}
|
|
||||||
|
{% if package.checkPerm(current_user, "EDIT_PACKAGE") and current_user not in package.maintainers %}
|
||||||
|
<h3>
|
||||||
|
{{ _("Added by Editor") }}
|
||||||
|
<i class="ml-2 fas fa-user-edit"></i>
|
||||||
|
</h3>
|
||||||
|
{{ render_field(form.supported) }}
|
||||||
|
{{ render_field(form.unsupported) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{{ render_submit_field(form.submit, class_="mt-4 btn btn-primary") }}
|
{{ render_submit_field(form.submit, class_="mt-4 btn btn-primary") }}
|
||||||
</form>
|
</form>
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
import sqlalchemy.orm
|
||||||
from flask import abort, redirect, url_for, request
|
from flask import abort, redirect, url_for, request
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from sqlalchemy import or_, and_
|
from sqlalchemy import or_, and_
|
||||||
@ -136,14 +137,14 @@ def post_bot_message(package: Package, title: str, message: str):
|
|||||||
thread.replies.append(reply)
|
thread.replies.append(reply)
|
||||||
|
|
||||||
|
|
||||||
def get_games_from_csv(csv: str) -> List[Package]:
|
def get_games_from_csv(session: sqlalchemy.orm.Session, csv: str) -> List[Package]:
|
||||||
retval = []
|
retval = []
|
||||||
supported_games_raw = csv.split(",")
|
supported_games_raw = csv.split(",")
|
||||||
for game_name in supported_games_raw:
|
for game_name in supported_games_raw:
|
||||||
game_name = game_name.strip()
|
game_name = game_name.strip()
|
||||||
if game_name.endswith("_game"):
|
if game_name.endswith("_game"):
|
||||||
game_name = game_name[:-5]
|
game_name = game_name[:-5]
|
||||||
games = Package.query.filter(and_(Package.state==PackageState.APPROVED, Package.type==PackageType.GAME,
|
games = session.query(Package).filter(and_(Package.state==PackageState.APPROVED, Package.type==PackageType.GAME,
|
||||||
or_(Package.name==game_name, Package.name==game_name + "_game"))).all()
|
or_(Package.name==game_name, Package.name==game_name + "_game"))).all()
|
||||||
retval.extend(games)
|
retval.extend(games)
|
||||||
|
|
||||||
|
24
migrations/versions/8425c06b7d77_.py
Normal file
24
migrations/versions/8425c06b7d77_.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 8425c06b7d77
|
||||||
|
Revises: 8807a5279793
|
||||||
|
Create Date: 2022-06-25 00:26:29.841145
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '8425c06b7d77'
|
||||||
|
down_revision = '8807a5279793'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('package', sa.Column('enable_game_support_detection', sa.Boolean(), nullable=False, server_default="true"))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('package', 'enable_game_support_detection')
|
Loading…
Reference in New Issue
Block a user