From 723994322b1a99a27d5b76753760abdbe737357a Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Fri, 19 May 2023 19:19:47 +0100 Subject: [PATCH] Refactor todo blueprint --- app/blueprints/todo/__init__.py | 306 +------------------------------- app/blueprints/todo/editor.py | 222 +++++++++++++++++++++++ app/blueprints/todo/user.py | 129 ++++++++++++++ 3 files changed, 353 insertions(+), 304 deletions(-) create mode 100644 app/blueprints/todo/editor.py create mode 100644 app/blueprints/todo/user.py diff --git a/app/blueprints/todo/__init__.py b/app/blueprints/todo/__init__.py index f53dbe15..0b51f02f 100644 --- a/app/blueprints/todo/__init__.py +++ b/app/blueprints/todo/__init__.py @@ -13,312 +13,10 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import sys -from celery import uuid -from flask import * -from flask_login import current_user, login_required -from sqlalchemy import or_, and_ -from app.models import * -from app.querybuilder import QueryBuilder -from app.utils import get_int_or_abort, addNotification, addAuditLog, isYes -from app.tasks.importtasks import makeVCSRelease +from flask import Blueprint bp = Blueprint("todo", __name__) -@bp.route("/todo/", methods=["GET", "POST"]) -@login_required -def view_editor(): - canApproveNew = Permission.APPROVE_NEW.check(current_user) - canApproveRel = Permission.APPROVE_RELEASE.check(current_user) - canApproveScn = Permission.APPROVE_SCREENSHOT.check(current_user) - - packages = None - wip_packages = None - if canApproveNew: - packages = Package.query.filter_by(state=PackageState.READY_FOR_REVIEW) \ - .order_by(db.desc(Package.created_at)).all() - wip_packages = Package.query.filter(or_(Package.state==PackageState.WIP, Package.state==PackageState.CHANGES_NEEDED)) \ - .order_by(db.desc(Package.created_at)).all() - - releases = None - if canApproveRel: - releases = PackageRelease.query.filter_by(approved=False).all() - - screenshots = None - if canApproveScn: - screenshots = PackageScreenshot.query.filter_by(approved=False).all() - - if not canApproveNew and not canApproveRel and not canApproveScn: - abort(403) - - if request.method == "POST": - if request.form["action"] == "screenshots_approve_all": - if not canApproveScn: - abort(403) - - PackageScreenshot.query.update({ "approved": True }) - db.session.commit() - return redirect(url_for("todo.view_editor")) - else: - abort(400) - - license_needed = Package.query \ - .filter(Package.state.in_([PackageState.READY_FOR_REVIEW, PackageState.APPROVED])) \ - .filter(or_(Package.license.has(License.name.like("Other %")), - Package.media_license.has(License.name.like("Other %")))) \ - .all() - - total_packages = Package.query.filter_by(state=PackageState.APPROVED).count() - total_to_tag = Package.query.filter_by(state=PackageState.APPROVED, tags=None).count() - - unfulfilled_meta_packages = MetaPackage.query \ - .filter(~ MetaPackage.packages.any(state=PackageState.APPROVED)) \ - .filter(MetaPackage.dependencies.any(Dependency.depender.has(state=PackageState.APPROVED), optional=False)) \ - .order_by(db.asc(MetaPackage.name)).count() - - audit_log = AuditLogEntry.query \ - .filter(AuditLogEntry.package.has()) \ - .order_by(db.desc(AuditLogEntry.created_at)) \ - .limit(20).all() - - return render_template("todo/editor.html", current_tab="editor", - packages=packages, wip_packages=wip_packages, releases=releases, screenshots=screenshots, - canApproveNew=canApproveNew, canApproveRel=canApproveRel, canApproveScn=canApproveScn, - license_needed=license_needed, total_packages=total_packages, total_to_tag=total_to_tag, - unfulfilled_meta_packages=unfulfilled_meta_packages, audit_log=audit_log) - - -@bp.route("/todo/topics/") -@login_required -def topics(): - qb = QueryBuilder(request.args) - qb.setSortIfNone("date") - query = qb.buildTopicQuery() - - tmp_q = ForumTopic.query - if not qb.show_discarded: - tmp_q = tmp_q.filter_by(discarded=False) - total = tmp_q.count() - topic_count = query.count() - - page = get_int_or_abort(request.args.get("page"), 1) - num = get_int_or_abort(request.args.get("n"), 100) - if num > 100 and not current_user.rank.atLeast(UserRank.APPROVER): - num = 100 - - query = query.paginate(page=page, per_page=num) - next_url = url_for("todo.topics", page=query.next_num, query=qb.search, - show_discarded=qb.show_discarded, n=num, sort=qb.order_by) \ - if query.has_next else None - prev_url = url_for("todo.topics", page=query.prev_num, query=qb.search, - show_discarded=qb.show_discarded, n=num, sort=qb.order_by) \ - if query.has_prev else None - - return render_template("todo/topics.html", current_tab="topics", topics=query.items, total=total, - topic_count=topic_count, query=qb.search, show_discarded=qb.show_discarded, - next_url=next_url, prev_url=prev_url, page=page, page_max=query.pages, - n=num, sort_by=qb.order_by) - - -@bp.route("/todo/tags/") -@login_required -def tags(): - qb = QueryBuilder(request.args) - qb.setSortIfNone("score", "desc") - query = qb.buildPackageQuery() - - only_no_tags = isYes(request.args.get("no_tags")) - if only_no_tags: - query = query.filter(Package.tags==None) - - tags = Tag.query.order_by(db.asc(Tag.title)).all() - - return render_template("todo/tags.html", current_tab="tags", packages=query.all(), \ - tags=tags, only_no_tags=only_no_tags) - - -@bp.route("/user/tags/") -def tags_user(): - return redirect(url_for('todo.tags', author=current_user.username)) - - -@bp.route("/todo/modnames/") -@login_required -def modnames(): - mnames = MetaPackage.query \ - .filter(~ MetaPackage.packages.any(state=PackageState.APPROVED)) \ - .filter(MetaPackage.dependencies.any(Dependency.depender.has(state=PackageState.APPROVED), optional=False)) \ - .order_by(db.asc(MetaPackage.name)).all() - - return render_template("todo/modnames.html", modnames=mnames) - - -@bp.route("/user/todo/") -@bp.route("/users//todo/") -@login_required -def view_user(username=None): - if username is None: - return redirect(url_for("todo.view_user", username=current_user.username)) - - user : User = User.query.filter_by(username=username).first() - if not user: - abort(404) - - if current_user != user and not current_user.rank.atLeast(UserRank.APPROVER): - abort(403) - - unapproved_packages = user.packages \ - .filter(or_(Package.state == PackageState.WIP, - Package.state == PackageState.CHANGES_NEEDED)) \ - .order_by(db.asc(Package.created_at)).all() - - packages_with_no_screenshots = user.maintained_packages.filter( - ~Package.screenshots.any(), Package.state == PackageState.APPROVED).all() - - packages_with_small_screenshots = user.maintained_packages \ - .filter(Package.state != PackageState.DELETED, - Package.screenshots.any(and_(PackageScreenshot.width < PackageScreenshot.SOFT_MIN_SIZE[0], - PackageScreenshot.height < PackageScreenshot.SOFT_MIN_SIZE[1]))) \ - .all() - - outdated_packages = user.maintained_packages \ - .filter(Package.state != PackageState.DELETED, - Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \ - .order_by(db.asc(Package.title)).all() - - topics_to_add = ForumTopic.query \ - .filter_by(author_id=user.id) \ - .filter(~ db.exists().where(Package.forums == ForumTopic.topic_id)) \ - .order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \ - .all() - - needs_tags = user.maintained_packages \ - .filter(Package.state != PackageState.DELETED, ~Package.tags.any()) \ - .order_by(db.asc(Package.title)).all() - - return render_template("todo/user.html", current_tab="user", user=user, - unapproved_packages=unapproved_packages, outdated_packages=outdated_packages, - needs_tags=needs_tags, topics_to_add=topics_to_add, - packages_with_no_screenshots=packages_with_no_screenshots, - packages_with_small_screenshots=packages_with_small_screenshots, - screenshot_min_size=PackageScreenshot.HARD_MIN_SIZE, screenshot_rec_size=PackageScreenshot.SOFT_MIN_SIZE) - - -@bp.route("/users//update-configs/apply-all/", methods=["POST"]) -@login_required -def apply_all_updates(username): - user: User = User.query.filter_by(username=username).first() - if not user: - abort(404) - - if current_user != user and not current_user.rank.atLeast(UserRank.EDITOR): - abort(403) - - outdated_packages = user.maintained_packages \ - .filter(Package.state != PackageState.DELETED, - Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \ - .order_by(db.asc(Package.title)).all() - - for package in outdated_packages: - if not package.checkPerm(current_user, Permission.MAKE_RELEASE): - continue - - if package.releases.filter(or_(PackageRelease.task_id.isnot(None), - PackageRelease.commit_hash==package.update_config.last_commit)).count() > 0: - continue - - title = package.update_config.get_title() - ref = package.update_config.get_ref() - - rel = PackageRelease() - rel.package = package - rel.title = title - rel.url = "" - rel.task_id = uuid() - db.session.add(rel) - db.session.commit() - - makeVCSRelease.apply_async((rel.id, ref), - task_id=rel.task_id) - - msg = "Created release {} (Applied all Git Update Detection)".format(rel.title) - addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, - rel.getURL("packages.create_edit"), package) - addAuditLog(AuditSeverity.NORMAL, current_user, msg, package.getURL("packages.view"), package) - db.session.commit() - - return redirect(url_for("todo.view_user", username=username)) - - -@bp.route("/todo/outdated/") -@login_required -def outdated(): - is_mtm_only = isYes(request.args.get("mtm")) - - query = db.session.query(Package).select_from(PackageUpdateConfig) \ - .filter(PackageUpdateConfig.outdated_at.isnot(None)) \ - .join(PackageUpdateConfig.package) \ - .filter(Package.state == PackageState.APPROVED) - - if is_mtm_only: - query = query.filter(Package.repo.ilike("%github.com/minetest-mods/%")) - - sort_by = request.args.get("sort") - if sort_by == "date": - query = query.order_by(db.desc(PackageUpdateConfig.outdated_at)) - else: - sort_by = "score" - query = query.order_by(db.desc(Package.score)) - - return render_template("todo/outdated.html", current_tab="outdated", - outdated_packages=query.all(), sort_by=sort_by, is_mtm_only=is_mtm_only) - - -@bp.route("/todo/screenshots/") -@login_required -def screenshots(): - is_mtm_only = isYes(request.args.get("mtm")) - - query = db.session.query(Package) \ - .filter(~Package.screenshots.any()) \ - .filter(Package.state == PackageState.APPROVED) - - if is_mtm_only: - query = query.filter(Package.repo.ilike("%github.com/minetest-mods/%")) - - sort_by = request.args.get("sort") - if sort_by == "date": - query = query.order_by(db.desc(Package.approved_at)) - else: - sort_by = "score" - query = query.order_by(db.desc(Package.score)) - - return render_template("todo/screenshots.html", current_tab="screenshots", - packages=query.all(), sort_by=sort_by, is_mtm_only=is_mtm_only) - - -@bp.route("/todo/mtver_support/") -@login_required -def mtver_support(): - is_mtm_only = isYes(request.args.get("mtm")) - - current_stable = MinetestRelease.query.filter(~MinetestRelease.name.like("%-dev")).order_by(db.desc(MinetestRelease.id)).first() - - query = db.session.query(Package) \ - .filter(~Package.releases.any(or_(PackageRelease.max_rel==None, PackageRelease.max_rel == current_stable))) \ - .filter(Package.state == PackageState.APPROVED) - - if is_mtm_only: - query = query.filter(Package.repo.ilike("%github.com/minetest-mods/%")) - - sort_by = request.args.get("sort") - if sort_by == "date": - query = query.order_by(db.desc(Package.approved_at)) - else: - sort_by = "score" - query = query.order_by(db.desc(Package.score)) - - return render_template("todo/mtver_support.html", current_tab="screenshots", - packages=query.all(), sort_by=sort_by, is_mtm_only=is_mtm_only, current_stable=current_stable) +from . import editor, user diff --git a/app/blueprints/todo/editor.py b/app/blueprints/todo/editor.py new file mode 100644 index 00000000..e1286156 --- /dev/null +++ b/app/blueprints/todo/editor.py @@ -0,0 +1,222 @@ +# ContentDB +# Copyright (C) 2018-23 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 . + + +from flask import redirect, url_for, abort, render_template, request +from flask_login import current_user, login_required +from sqlalchemy import or_ + +from app.models import Package, PackageState, PackageScreenshot, PackageUpdateConfig, ForumTopic, db, \ + PackageRelease, Permission, UserRank, License, MetaPackage, Dependency, AuditLogEntry, Tag, MinetestRelease +from app.querybuilder import QueryBuilder +from app.utils import get_int_or_abort, isYes +from . import bp + + +@bp.route("/todo/", methods=["GET", "POST"]) +@login_required +def view_editor(): + can_approve_new = Permission.APPROVE_NEW.check(current_user) + can_approve_rel = Permission.APPROVE_RELEASE.check(current_user) + can_approve_scn = Permission.APPROVE_SCREENSHOT.check(current_user) + + packages = None + wip_packages = None + if can_approve_new: + packages = Package.query.filter_by(state=PackageState.READY_FOR_REVIEW) \ + .order_by(db.desc(Package.created_at)).all() + wip_packages = Package.query \ + .filter(or_(Package.state == PackageState.WIP, Package.state == PackageState.CHANGES_NEEDED)) \ + .order_by(db.desc(Package.created_at)).all() + + releases = None + if can_approve_rel: + releases = PackageRelease.query.filter_by(approved=False).all() + + screenshots = None + if can_approve_scn: + screenshots = PackageScreenshot.query.filter_by(approved=False).all() + + if not can_approve_new and not can_approve_rel and not can_approve_scn: + abort(403) + + if request.method == "POST": + if request.form["action"] == "screenshots_approve_all": + if not can_approve_scn: + abort(403) + + PackageScreenshot.query.update({"approved": True}) + db.session.commit() + return redirect(url_for("todo.view_editor")) + else: + abort(400) + + license_needed = Package.query \ + .filter(Package.state.in_([PackageState.READY_FOR_REVIEW, PackageState.APPROVED])) \ + .filter(or_(Package.license.has(License.name.like("Other %")), + Package.media_license.has(License.name.like("Other %")))) \ + .all() + + total_packages = Package.query.filter_by(state=PackageState.APPROVED).count() + total_to_tag = Package.query.filter_by(state=PackageState.APPROVED, tags=None).count() + + unfulfilled_meta_packages = MetaPackage.query \ + .filter(~ MetaPackage.packages.any(state=PackageState.APPROVED)) \ + .filter(MetaPackage.dependencies.any(Dependency.depender.has(state=PackageState.APPROVED), optional=False)) \ + .order_by(db.asc(MetaPackage.name)).count() + + audit_log = AuditLogEntry.query \ + .filter(AuditLogEntry.package.has()) \ + .order_by(db.desc(AuditLogEntry.created_at)) \ + .limit(20).all() + + return render_template("todo/editor.html", current_tab="editor", + packages=packages, wip_packages=wip_packages, releases=releases, screenshots=screenshots, + canApproveNew=can_approve_new, canApproveRel=can_approve_rel, canApproveScn=can_approve_scn, + license_needed=license_needed, total_packages=total_packages, total_to_tag=total_to_tag, + unfulfilled_meta_packages=unfulfilled_meta_packages, audit_log=audit_log) + + +@bp.route("/todo/topics/") +@login_required +def topics(): + qb = QueryBuilder(request.args) + qb.setSortIfNone("date") + query = qb.buildTopicQuery() + + tmp_q = ForumTopic.query + if not qb.show_discarded: + tmp_q = tmp_q.filter_by(discarded=False) + total = tmp_q.count() + topic_count = query.count() + + page = get_int_or_abort(request.args.get("page"), 1) + num = get_int_or_abort(request.args.get("n"), 100) + if num > 100 and not current_user.rank.atLeast(UserRank.APPROVER): + num = 100 + + query = query.paginate(page=page, per_page=num) + next_url = url_for("todo.topics", page=query.next_num, query=qb.search, + show_discarded=qb.show_discarded, n=num, sort=qb.order_by) \ + if query.has_next else None + prev_url = url_for("todo.topics", page=query.prev_num, query=qb.search, + show_discarded=qb.show_discarded, n=num, sort=qb.order_by) \ + if query.has_prev else None + + return render_template("todo/topics.html", current_tab="topics", topics=query.items, total=total, + topic_count=topic_count, query=qb.search, show_discarded=qb.show_discarded, + next_url=next_url, prev_url=prev_url, page=page, page_max=query.pages, + n=num, sort_by=qb.order_by) + + +@bp.route("/todo/tags/") +@login_required +def tags(): + qb = QueryBuilder(request.args) + qb.setSortIfNone("score", "desc") + query = qb.buildPackageQuery() + + only_no_tags = isYes(request.args.get("no_tags")) + if only_no_tags: + query = query.filter(Package.tags == None) + + tags = Tag.query.order_by(db.asc(Tag.title)).all() + + return render_template("todo/tags.html", current_tab="tags", packages=query.all(), + tags=tags, only_no_tags=only_no_tags) + + +@bp.route("/todo/modnames/") +@login_required +def modnames(): + mnames = MetaPackage.query \ + .filter(~ MetaPackage.packages.any(state=PackageState.APPROVED)) \ + .filter(MetaPackage.dependencies.any(Dependency.depender.has(state=PackageState.APPROVED), optional=False)) \ + .order_by(db.asc(MetaPackage.name)).all() + + return render_template("todo/modnames.html", modnames=mnames) + + +@bp.route("/todo/outdated/") +@login_required +def outdated(): + is_mtm_only = isYes(request.args.get("mtm")) + + query = db.session.query(Package).select_from(PackageUpdateConfig) \ + .filter(PackageUpdateConfig.outdated_at.isnot(None)) \ + .join(PackageUpdateConfig.package) \ + .filter(Package.state == PackageState.APPROVED) + + if is_mtm_only: + query = query.filter(Package.repo.ilike("%github.com/minetest-mods/%")) + + sort_by = request.args.get("sort") + if sort_by == "date": + query = query.order_by(db.desc(PackageUpdateConfig.outdated_at)) + else: + sort_by = "score" + query = query.order_by(db.desc(Package.score)) + + return render_template("todo/outdated.html", current_tab="outdated", + outdated_packages=query.all(), sort_by=sort_by, is_mtm_only=is_mtm_only) + + +@bp.route("/todo/screenshots/") +@login_required +def screenshots(): + is_mtm_only = isYes(request.args.get("mtm")) + + query = db.session.query(Package) \ + .filter(~Package.screenshots.any()) \ + .filter(Package.state == PackageState.APPROVED) + + if is_mtm_only: + query = query.filter(Package.repo.ilike("%github.com/minetest-mods/%")) + + sort_by = request.args.get("sort") + if sort_by == "date": + query = query.order_by(db.desc(Package.approved_at)) + else: + sort_by = "score" + query = query.order_by(db.desc(Package.score)) + + return render_template("todo/screenshots.html", current_tab="screenshots", + packages=query.all(), sort_by=sort_by, is_mtm_only=is_mtm_only) + + +@bp.route("/todo/mtver_support/") +@login_required +def mtver_support(): + is_mtm_only = isYes(request.args.get("mtm")) + + current_stable = MinetestRelease.query.filter(~MinetestRelease.name.like("%-dev")).order_by(db.desc(MinetestRelease.id)).first() + + query = db.session.query(Package) \ + .filter(~Package.releases.any(or_(PackageRelease.max_rel==None, PackageRelease.max_rel == current_stable))) \ + .filter(Package.state == PackageState.APPROVED) + + if is_mtm_only: + query = query.filter(Package.repo.ilike("%github.com/minetest-mods/%")) + + sort_by = request.args.get("sort") + if sort_by == "date": + query = query.order_by(db.desc(Package.approved_at)) + else: + sort_by = "score" + query = query.order_by(db.desc(Package.score)) + + return render_template("todo/mtver_support.html", current_tab="screenshots", + packages=query.all(), sort_by=sort_by, is_mtm_only=is_mtm_only, current_stable=current_stable) diff --git a/app/blueprints/todo/user.py b/app/blueprints/todo/user.py new file mode 100644 index 00000000..a035e75d --- /dev/null +++ b/app/blueprints/todo/user.py @@ -0,0 +1,129 @@ +# ContentDB +# Copyright (C) 2018-23 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 . + +from celery import uuid +from flask import redirect, url_for, abort, render_template +from flask_login import current_user, login_required +from sqlalchemy import or_, and_ + +from app.models import User, Package, PackageState, PackageScreenshot, PackageUpdateConfig, ForumTopic, db, \ + PackageRelease, Permission, NotificationType, AuditSeverity, UserRank +from app.tasks.importtasks import makeVCSRelease +from app.utils import addNotification, addAuditLog + +from . import bp + + +@bp.route("/user/tags/") +def tags_user(): + return redirect(url_for('todo.tags', author=current_user.username)) + + +@bp.route("/user/todo/") +@bp.route("/users//todo/") +@login_required +def view_user(username=None): + if username is None: + return redirect(url_for("todo.view_user", username=current_user.username)) + + user: User = User.query.filter_by(username=username).first() + if not user: + abort(404) + + if current_user != user and not current_user.rank.atLeast(UserRank.APPROVER): + abort(403) + + unapproved_packages = user.packages \ + .filter(or_(Package.state == PackageState.WIP, + Package.state == PackageState.CHANGES_NEEDED)) \ + .order_by(db.asc(Package.created_at)).all() + + packages_with_no_screenshots = user.maintained_packages.filter( + ~Package.screenshots.any(), Package.state == PackageState.APPROVED).all() + + packages_with_small_screenshots = user.maintained_packages \ + .filter(Package.state != PackageState.DELETED, + Package.screenshots.any(and_(PackageScreenshot.width < PackageScreenshot.SOFT_MIN_SIZE[0], + PackageScreenshot.height < PackageScreenshot.SOFT_MIN_SIZE[1]))) \ + .all() + + outdated_packages = user.maintained_packages \ + .filter(Package.state != PackageState.DELETED, + Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \ + .order_by(db.asc(Package.title)).all() + + topics_to_add = ForumTopic.query \ + .filter_by(author_id=user.id) \ + .filter(~ db.exists().where(Package.forums == ForumTopic.topic_id)) \ + .order_by(db.asc(ForumTopic.name), db.asc(ForumTopic.title)) \ + .all() + + needs_tags = user.maintained_packages \ + .filter(Package.state != PackageState.DELETED, ~Package.tags.any()) \ + .order_by(db.asc(Package.title)).all() + + return render_template("todo/user.html", current_tab="user", user=user, + unapproved_packages=unapproved_packages, outdated_packages=outdated_packages, + needs_tags=needs_tags, topics_to_add=topics_to_add, + packages_with_no_screenshots=packages_with_no_screenshots, + packages_with_small_screenshots=packages_with_small_screenshots, + screenshot_min_size=PackageScreenshot.HARD_MIN_SIZE, screenshot_rec_size=PackageScreenshot.SOFT_MIN_SIZE) + + +@bp.route("/users//update-configs/apply-all/", methods=["POST"]) +@login_required +def apply_all_updates(username): + user: User = User.query.filter_by(username=username).first() + if not user: + abort(404) + + if current_user != user and not current_user.rank.atLeast(UserRank.EDITOR): + abort(403) + + outdated_packages = user.maintained_packages \ + .filter(Package.state != PackageState.DELETED, + Package.update_config.has(PackageUpdateConfig.outdated_at.isnot(None))) \ + .order_by(db.asc(Package.title)).all() + + for package in outdated_packages: + if not package.checkPerm(current_user, Permission.MAKE_RELEASE): + continue + + if package.releases.filter(or_(PackageRelease.task_id.isnot(None), + PackageRelease.commit_hash == package.update_config.last_commit)).count() > 0: + continue + + title = package.update_config.get_title() + ref = package.update_config.get_ref() + + rel = PackageRelease() + rel.package = package + rel.title = title + rel.url = "" + rel.task_id = uuid() + db.session.add(rel) + db.session.commit() + + makeVCSRelease.apply_async((rel.id, ref), + task_id=rel.task_id) + + msg = "Created release {} (Applied all Git Update Detection)".format(rel.title) + addNotification(package.maintainers, current_user, NotificationType.PACKAGE_EDIT, msg, + rel.getURL("packages.create_edit"), package) + addAuditLog(AuditSeverity.NORMAL, current_user, msg, package.getURL("packages.view"), package) + db.session.commit() + + return redirect(url_for("todo.view_user", username=username))