From e2708933d33b0e33bdbed75245aa8ecbb7648df2 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 26 Jan 2022 19:12:42 +0000 Subject: [PATCH] Clean up admin blueprint --- app/blueprints/admin/actions.py | 78 +++++++++++++------------- app/blueprints/admin/admin.py | 23 +++++--- app/blueprints/admin/audit.py | 4 +- app/blueprints/admin/email.py | 13 ++--- app/blueprints/admin/licenseseditor.py | 18 +++--- app/blueprints/admin/tagseditor.py | 16 +++--- app/blueprints/admin/versioneditor.py | 14 +++-- app/blueprints/admin/warningseditor.py | 17 +++--- 8 files changed, 97 insertions(+), 86 deletions(-) diff --git a/app/blueprints/admin/actions.py b/app/blueprints/admin/actions.py index 8a742f6..d584e39 100644 --- a/app/blueprints/admin/actions.py +++ b/app/blueprints/admin/actions.py @@ -20,10 +20,11 @@ from typing import List import requests from celery import group -from flask import * +from flask import redirect, url_for, flash, current_app from sqlalchemy import or_, and_ -from app.models import * +from app.models import PackageRelease, db, Package, PackageState, PackageScreenshot, MetaPackage, User, \ + NotificationType, PackageUpdateConfig, License, UserRank, PackageType from app.tasks.forumtasks import importTopicList, checkAllForumAccounts from app.tasks.importtasks import importRepoScreenshot, checkZipRelease, check_for_updates from app.utils import addNotification, get_system_user @@ -31,6 +32,7 @@ from app.utils.image import get_image_size actions = {} + def action(title: str): def func(f): name = f.__name__ @@ -43,13 +45,15 @@ def action(title: str): return func + @action("Delete stuck releases") def del_stuck_releases(): - PackageRelease.query.filter(PackageRelease.task_id != None).delete() + PackageRelease.query.filter(PackageRelease.task_id.isnot(None)).delete() db.session.commit() return redirect(url_for("admin.admin_page")) -@action("Check releases") + +@action("Check ZIP releases") def check_releases(): releases = PackageRelease.query.filter(PackageRelease.url.like("/uploads/%")).all() @@ -65,10 +69,11 @@ def check_releases(): return redirect(url_for("todo.view_editor")) -@action("Reimport packages") + +@action("Check the first release of all packages") def reimport_packages(): tasks = [] - for package in Package.query.filter(Package.state!=PackageState.DELETED).all(): + for package in Package.query.filter(Package.state != PackageState.DELETED).all(): release = package.releases.first() if release: tasks.append(checkZipRelease.s(release.id, release.file_path)) @@ -81,42 +86,46 @@ def reimport_packages(): return redirect(url_for("todo.view_editor")) -@action("Import topic list") + +@action("Import forum topic list") def import_topic_list(): task = importTopicList.delay() return redirect(url_for("tasks.check", id=task.id, r=url_for("todo.topics"))) + @action("Check all forum accounts") def check_all_forum_accounts(): task = checkAllForumAccounts.delay() return redirect(url_for("tasks.check", id=task.id, r=url_for("admin.admin_page"))) + @action("Import screenshots") def import_screenshots(): packages = Package.query \ - .filter(Package.state!=PackageState.DELETED) \ - .outerjoin(PackageScreenshot, Package.id==PackageScreenshot.package_id) \ - .filter(PackageScreenshot.id==None) \ + .filter(Package.state != PackageState.DELETED) \ + .outerjoin(PackageScreenshot, Package.id == PackageScreenshot.package_id) \ + .filter(PackageScreenshot.id.is_(None)) \ .all() for package in packages: importRepoScreenshot.delay(package.id) return redirect(url_for("admin.admin_page")) -@action("Clean uploads") + +@action("Remove unused uploads") def clean_uploads(): - upload_dir = app.config['UPLOAD_DIR'] + upload_dir = current_app.config['UPLOAD_DIR'] (_, _, filenames) = next(os.walk(upload_dir)) existing_uploads = set(filenames) if len(existing_uploads) != 0: - def getURLsFromDB(column): - results = db.session.query(column).filter(column != None, column != "").all() + def get_filenames_from_column(column): + results = db.session.query(column).filter(column.isnot(None), column != "").all() return set([os.path.basename(x[0]) for x in results]) - release_urls = getURLsFromDB(PackageRelease.url) - screenshot_urls = getURLsFromDB(PackageScreenshot.url) + release_urls = get_filenames_from_column(PackageRelease.url) + screenshot_urls = get_filenames_from_column(PackageScreenshot.url) db_urls = release_urls.union(screenshot_urls) unreachable = existing_uploads.difference(db_urls) @@ -135,7 +144,8 @@ def clean_uploads(): return redirect(url_for("admin.admin_page")) -@action("Delete metapackages") + +@action("Delete unused metapackages") def del_meta_packages(): query = MetaPackage.query.filter(~MetaPackage.dependencies.any(), ~MetaPackage.packages.any()) count = query.count() @@ -145,6 +155,7 @@ def del_meta_packages(): flash("Deleted " + str(count) + " unused meta packages", "success") return redirect(url_for("admin.admin_page")) + @action("Delete removed packages") def del_removed_packages(): query = Package.query.filter_by(state=PackageState.DELETED) @@ -157,24 +168,6 @@ def del_removed_packages(): flash("Deleted {} soft deleted packages packages".format(count), "success") return redirect(url_for("admin.admin_page")) -@action("Add update config") -def add_update_config(): - added = 0 - for pkg in Package.query.filter(Package.repo != None, Package.releases.any(), Package.update_config == None).all(): - pkg.update_config = PackageUpdateConfig() - pkg.update_config.auto_created = True - - release: PackageRelease = pkg.releases.first() - if release and release.commit_hash: - pkg.update_config.last_commit = release.commit_hash - - db.session.add(pkg.update_config) - added += 1 - - db.session.commit() - - flash("Added {} update configs".format(added), "success") - return redirect(url_for("admin.admin_page")) @action("Run update configs") def run_update_config(): @@ -183,6 +176,7 @@ def run_update_config(): flash("Started update configs", "success") return redirect(url_for("admin.admin_page")) + def _package_list(packages: List[str]): # Who needs translations? if len(packages) >= 3: @@ -192,14 +186,15 @@ def _package_list(packages: List[str]): packages_list = " and ".join(packages) return packages_list + @action("Send WIP package notification") def remind_wip(): - users = User.query.filter(User.packages.any(or_(Package.state==PackageState.WIP, Package.state==PackageState.CHANGES_NEEDED))) + users = User.query.filter(User.packages.any(or_(Package.state == PackageState.WIP, Package.state == PackageState.CHANGES_NEEDED))) system_user = get_system_user() for user in users: packages = db.session.query(Package.title).filter( - Package.author_id==user.id, - or_(Package.state==PackageState.WIP, Package.state==PackageState.CHANGES_NEEDED)) \ + Package.author_id == user.id, + or_(Package.state == PackageState.WIP, Package.state==PackageState.CHANGES_NEEDED)) \ .all() packages = [pkg[0] for pkg in packages] @@ -213,6 +208,7 @@ def remind_wip(): url_for('todo.view_user', username=user.username)) db.session.commit() + @action("Send outdated package notification") def remind_outdated(): users = User.query.filter(User.maintained_packages.any( @@ -233,6 +229,7 @@ def remind_outdated(): db.session.commit() + @action("Import licenses from SPDX") def import_licenses(): renames = { @@ -283,7 +280,8 @@ def import_licenses(): @action("Delete inactive users") def delete_inactive_users(): - users = User.query.filter(User.is_active==False, User.packages==None, User.forum_topics==None, User.rank==UserRank.NOT_JOINED).all() + users = User.query.filter(User.is_active == False, User.packages.is_(None), User.forum_topics.is_(None), + User.rank == UserRank.NOT_JOINED).all() for user in users: db.session.delete(user) db.session.commit() @@ -313,7 +311,7 @@ def remind_video_url(): @action("Update screenshot sizes") -def remind_video_url(): +def update_screenshot_sizes(): import sys for screenshot in PackageScreenshot.query.all(): diff --git a/app/blueprints/admin/admin.py b/app/blueprints/admin/admin.py index 8345dfb..eea4d95 100644 --- a/app/blueprints/admin/admin.py +++ b/app/blueprints/admin/admin.py @@ -14,10 +14,10 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from flask import * +from flask import redirect, render_template, url_for, request, flash from flask_login import current_user, login_user from flask_wtf import FlaskForm -from wtforms import * +from wtforms import StringField, SubmitField from wtforms.validators import InputRequired, Length from app.utils import rank_required, addAuditLog, addNotification, get_system_user from . import bp @@ -48,9 +48,10 @@ def admin_page(): else: flash("Unknown action: " + action, "danger") - deleted_packages = Package.query.filter(Package.state==PackageState.DELETED).all() + deleted_packages = Package.query.filter(Package.state == PackageState.DELETED).all() return render_template("admin/list.html", deleted_packages=deleted_packages, actions=actions) + class SwitchUserForm(FlaskForm): username = StringField("Username") submit = SubmitField("Switch") @@ -69,14 +70,13 @@ def switch_user(): else: flash("Unable to login as user", "danger") - # Process GET or invalid POST return render_template("admin/switch_user.html", form=form) class SendNotificationForm(FlaskForm): - title = StringField("Title", [InputRequired(), Length(1, 300)]) - url = StringField("URL", [InputRequired(), Length(1, 100)], default="/") + title = StringField("Title", [InputRequired(), Length(1, 300)]) + url = StringField("URL", [InputRequired(), Length(1, 100)], default="/") submit = SubmitField("Send") @@ -86,7 +86,7 @@ def send_bulk_notification(): form = SendNotificationForm(request.form) if form.validate_on_submit(): addAuditLog(AuditSeverity.MODERATION, current_user, - "Sent bulk notification", None, None, form.title.data) + "Sent bulk notification", url_for("admin.admin_page"), None, form.title.data) users = User.query.filter(User.rank >= UserRank.NEW_MEMBER).all() addNotification(users, get_system_user(), NotificationType.OTHER, form.title.data, form.url.data, None) @@ -121,5 +121,10 @@ def restore(): db.session.commit() return redirect(package.getURL("packages.view")) - deleted_packages = Package.query.filter(Package.state==PackageState.DELETED).join(Package.author).order_by(db.asc(User.username), db.asc(Package.name)).all() - return render_template("admin/restore.html", deleted_packages=deleted_packages) \ No newline at end of file + deleted_packages = Package.query \ + .filter(Package.state == PackageState.DELETED) \ + .join(Package.author) \ + .order_by(db.asc(User.username), db.asc(Package.name)) \ + .all() + + return render_template("admin/restore.html", deleted_packages=deleted_packages) diff --git a/app/blueprints/admin/audit.py b/app/blueprints/admin/audit.py index cf8a8f9..26c20ef 100644 --- a/app/blueprints/admin/audit.py +++ b/app/blueprints/admin/audit.py @@ -41,6 +41,6 @@ def audit(): @bp.route("/admin/audit//") @rank_required(UserRank.MODERATOR) -def audit_view(id): - entry = AuditLogEntry.query.get(id) +def audit_view(id_): + entry = AuditLogEntry.query.get(id_) return render_template("admin/audit_view.html", entry=entry) diff --git a/app/blueprints/admin/email.py b/app/blueprints/admin/email.py index 99b8aad..b0b54a0 100644 --- a/app/blueprints/admin/email.py +++ b/app/blueprints/admin/email.py @@ -14,18 +14,17 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . - -from flask import * +from flask import request, abort, url_for, redirect, render_template, flash from flask_login import current_user from flask_wtf import FlaskForm -from wtforms import * -from wtforms.validators import * +from wtforms import TextAreaField, SubmitField, StringField +from wtforms.validators import InputRequired, Length from app.markdown import render_markdown -from app.models import * from app.tasks.emails import send_user_email from app.utils import rank_required, addAuditLog from . import bp +from ...models import UserRank, User, AuditSeverity class SendEmailForm(FlaskForm): @@ -67,11 +66,11 @@ def send_bulk_email(): form = SendEmailForm(request.form) if form.validate_on_submit(): addAuditLog(AuditSeverity.MODERATION, current_user, - "Sent bulk email", None, None, form.text.data) + "Sent bulk email", url_for("admin.admin_page"), None, form.text.data) text = form.text.data html = render_markdown(text) - for user in User.query.filter(User.email != None).all(): + for user in User.query.filter(User.email.isnot(None)).all(): send_user_email.delay(user.email, user.locale or "en", form.subject.data, text, html) return redirect(url_for("admin.admin_page")) diff --git a/app/blueprints/admin/licenseseditor.py b/app/blueprints/admin/licenseseditor.py index 40ef3c6..e4a5be1 100644 --- a/app/blueprints/admin/licenseseditor.py +++ b/app/blueprints/admin/licenseseditor.py @@ -15,15 +15,15 @@ # along with this program. If not, see . -from flask import * +from flask import redirect, render_template, abort, url_for, request, flash from flask_wtf import FlaskForm -from wtforms import * +from wtforms import StringField, BooleanField, SubmitField from wtforms.fields.html5 import URLField -from wtforms.validators import * +from wtforms.validators import InputRequired, Length, Optional -from app.models import * from app.utils import rank_required, nonEmptyOrNone from . import bp +from ...models import UserRank, License, db @bp.route("/licenses/") @@ -31,11 +31,13 @@ from . import bp def license_list(): return render_template("admin/licenses/list.html", licenses=License.query.order_by(db.asc(License.name)).all()) + class LicenseForm(FlaskForm): - name = StringField("Name", [InputRequired(), Length(3,100)]) - is_foss = BooleanField("Is FOSS") - url = URLField("URL", [Optional], filters=[nonEmptyOrNone]) - submit = SubmitField("Save") + name = StringField("Name", [InputRequired(), Length(3, 100)]) + is_foss = BooleanField("Is FOSS") + url = URLField("URL", [Optional], filters=[nonEmptyOrNone]) + submit = SubmitField("Save") + @bp.route("/licenses/new/", methods=["GET", "POST"]) @bp.route("/licenses//edit/", methods=["GET", "POST"]) diff --git a/app/blueprints/admin/tagseditor.py b/app/blueprints/admin/tagseditor.py index ad0d731..02f2291 100644 --- a/app/blueprints/admin/tagseditor.py +++ b/app/blueprints/admin/tagseditor.py @@ -15,14 +15,14 @@ # along with this program. If not, see . -from flask import * +from flask import redirect, render_template, abort, url_for, request from flask_login import current_user, login_required from flask_wtf import FlaskForm -from wtforms import * -from wtforms.validators import * +from wtforms import StringField, TextAreaField, BooleanField, SubmitField +from wtforms.validators import InputRequired, Length, Optional, Regexp -from app.models import * from . import bp +from ...models import Permission, Tag, db @bp.route("/tags/") @@ -40,12 +40,14 @@ def tag_list(): return render_template("admin/tags/list.html", tags=query.all()) + class TagForm(FlaskForm): - title = StringField("Title", [InputRequired(), Length(3,100)]) + title = StringField("Title", [InputRequired(), Length(3, 100)]) description = TextAreaField("Description", [Optional(), Length(0, 500)]) - name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")]) + name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")]) is_protected = BooleanField("Is Protected") - submit = SubmitField("Save") + submit = SubmitField("Save") + @bp.route("/tags/new/", methods=["GET", "POST"]) @bp.route("/tags//edit/", methods=["GET", "POST"]) diff --git a/app/blueprints/admin/versioneditor.py b/app/blueprints/admin/versioneditor.py index 62e1f1a..b3063b4 100644 --- a/app/blueprints/admin/versioneditor.py +++ b/app/blueprints/admin/versioneditor.py @@ -15,14 +15,14 @@ # along with this program. If not, see . -from flask import * +from flask import redirect, render_template, abort, url_for, request, flash from flask_wtf import FlaskForm -from wtforms import * -from wtforms.validators import * +from wtforms import StringField, IntegerField, SubmitField +from wtforms.validators import InputRequired, Length -from app.models import * from app.utils import rank_required from . import bp +from ...models import UserRank, MinetestRelease, db @bp.route("/versions/") @@ -30,10 +30,12 @@ from . import bp def version_list(): return render_template("admin/versions/list.html", versions=MinetestRelease.query.order_by(db.asc(MinetestRelease.id)).all()) + class VersionForm(FlaskForm): - name = StringField("Name", [InputRequired(), Length(3,100)]) + name = StringField("Name", [InputRequired(), Length(3, 100)]) protocol = IntegerField("Protocol") - submit = SubmitField("Save") + submit = SubmitField("Save") + @bp.route("/versions/new/", methods=["GET", "POST"]) @bp.route("/versions//edit/", methods=["GET", "POST"]) diff --git a/app/blueprints/admin/warningseditor.py b/app/blueprints/admin/warningseditor.py index f23b8e9..94a8df6 100644 --- a/app/blueprints/admin/warningseditor.py +++ b/app/blueprints/admin/warningseditor.py @@ -15,14 +15,14 @@ # along with this program. If not, see . -from flask import * +from flask import redirect, render_template, abort, url_for, request, flash from flask_wtf import FlaskForm -from wtforms import * -from wtforms.validators import * +from wtforms import StringField, TextAreaField, SubmitField +from wtforms.validators import InputRequired, Length, Optional, Regexp -from app.models import * from app.utils import rank_required from . import bp +from ...models import UserRank, ContentWarning, db @bp.route("/admin/warnings/") @@ -30,11 +30,14 @@ from . import bp def warning_list(): return render_template("admin/warnings/list.html", warnings=ContentWarning.query.order_by(db.asc(ContentWarning.title)).all()) + class WarningForm(FlaskForm): - title = StringField("Title", [InputRequired(), Length(3,100)]) + title = StringField("Title", [InputRequired(), Length(3, 100)]) description = TextAreaField("Description", [Optional(), Length(0, 500)]) - name = StringField("Name", [Optional(), Length(1, 20), Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")]) - submit = SubmitField("Save") + name = StringField("Name", [Optional(), Length(1, 20), + Regexp("^[a-z0-9_]", 0, "Lower case letters (a-z), digits (0-9), and underscores (_) only")]) + submit = SubmitField("Save") + @bp.route("/admin/warnings/new/", methods=["GET", "POST"]) @bp.route("/admin/warnings//edit/", methods=["GET", "POST"])