From 07d7282383da40ef65571a81e966ab6f13577fde Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 16 Jan 2021 00:34:09 +0000 Subject: [PATCH] Add APIs for tags and homepage --- app/blueprints/api/endpoints.py | 58 +++++++++++++++++++++++++++++++-- app/flatpages/help/api.md | 18 +++++++++- app/models/packages.py | 4 +++ 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/app/blueprints/api/endpoints.py b/app/blueprints/api/endpoints.py index 8afc2af3..54e1996d 100644 --- a/app/blueprints/api/endpoints.py +++ b/app/blueprints/api/endpoints.py @@ -25,6 +25,7 @@ from app.models import * from app.utils import is_package_page from app.markdown import render_markdown from app.querybuilder import QueryBuilder +from sqlalchemy.sql.expression import func @bp.route("/api/packages/") def packages(): @@ -32,9 +33,16 @@ def packages(): query = qb.buildPackageQuery() ver = qb.getMinetestVersion() - pkgs = [package.getAsDictionaryShort(current_app.config["BASE_URL"], version=ver) \ - for package in query.all()] - return jsonify([package for package in pkgs if package.get("release")]) + if request.args.get("fmt") == "keys": + return jsonify([package.getAsDictionaryKey() for package in query.all()]) + + def toJson(package: Package): + return package.getAsDictionaryShort(current_app.config["BASE_URL"], version=ver) + + pkgs = [toJson(package) for package in query.all()] + if "engine_version" in request.args or "protocol_version" in request.args: + pkgs = [package for package in pkgs if package.get("release")] + return jsonify(pkgs) @bp.route("/api/scores/") @@ -52,6 +60,50 @@ def package(package): return jsonify(package.getAsDictionary(current_app.config["BASE_URL"])) +@bp.route("/api/tags/") +def tags(): + return jsonify([tag.getAsDictionary() for tag in Tag.query.all() ]) + + +@bp.route("/api/homepage/") +def homepage(): + query = Package.query.filter_by(state=PackageState.APPROVED) + count = query.count() + + new = query.order_by(db.desc(Package.approved_at)).limit(4).all() + pop_mod = query.filter_by(type=PackageType.MOD).order_by(db.desc(Package.score)).limit(8).all() + pop_gam = query.filter_by(type=PackageType.GAME).order_by(db.desc(Package.score)).limit(8).all() + pop_txp = query.filter_by(type=PackageType.TXP).order_by(db.desc(Package.score)).limit(8).all() + high_reviewed = query.order_by(db.desc(Package.score - Package.score_downloads)) \ + .filter(Package.reviews.any()).limit(4).all() + + updated = db.session.query(Package).select_from(PackageRelease).join(Package) \ + .filter_by(state=PackageState.APPROVED) \ + .order_by(db.desc(PackageRelease.releaseDate)) \ + .limit(20).all() + updated = updated[:4] + + downloads_result = db.session.query(func.sum(Package.downloads)).one_or_none() + downloads = 0 if not downloads_result or not downloads_result[0] else downloads_result[0] + + tags = db.session.query(func.count(Tags.c.tag_id), Tag) \ + .select_from(Tag).outerjoin(Tags).group_by(Tag.id).order_by(db.asc(Tag.title)).all() + + def mapPackages(packages): + return [pkg.getAsDictionaryKey() for pkg in packages] + + return { + "count": count, + "downloads": downloads, + "new": mapPackages(new), + "updated": mapPackages(updated), + "pop_mod": mapPackages(pop_mod), + "pop_txp": mapPackages(pop_txp), + "pop_game": mapPackages(pop_gam), + "high_reviewed": mapPackages(high_reviewed), + } + + def resolve_package_deps(out, package, only_hard): id = package.getId() if id in out: diff --git a/app/flatpages/help/api.md b/app/flatpages/help/api.md index ec59849a..92bae88e 100644 --- a/app/flatpages/help/api.md +++ b/app/flatpages/help/api.md @@ -26,7 +26,20 @@ Tokens can be attained by visiting [Profile > "API Tokens"](/user/tokens/). * GET `/api/scores/` - See [Package Queries](#package-queries) * GET `/api/packages///` * GET `/api/packages///dependencies/` - * If query argument `only_hard` is present, only hard deps will be returned. + * If query argument `only_hard` is present, only hard deps will be returned. +* GET `/api/tags/` - List of: + * `name` - technical name + * `title` - human-readable title + * `description` - tag description or null +* GET `/api/homepage/` + * `count` - number of packages + * `downloads` - get number of downloads + * `new` - new packages + * `updated` - recently updated packages + * `pop_mod` - popular mods + * `pop_txp` - popular textures + * `pop_game` - popular games + * `high_reviewed` - highest reviewed ### Releases @@ -70,6 +83,9 @@ Supported query parameters: * `order` - Sort ascending (`asc`) or descending (`desc`). * `protocol_version` - Only show packages supported by this Minetest protocol version. * `engine_version` - Only show packages supported by this Minetest engine version, eg: `5.3.0`. +* `fmt` - How the response is formated. + * `keys` - author/name only. + * `short` - stuff needed for the Minetest client. ## Topic Queries diff --git a/app/models/packages.py b/app/models/packages.py index b0e76ff9..03676583 100644 --- a/app/models/packages.py +++ b/app/models/packages.py @@ -745,6 +745,10 @@ class Tag(db.Model): regex = re.compile("[^a-z_]") self.name = regex.sub("", self.title.lower().replace(" ", "_")) + def getAsDictionary(self): + description = self.description if self.description != "" else None + return { "name": self.name, "title": self.title, "description": description } + class MinetestRelease(db.Model): id = db.Column(db.Integer, primary_key=True)