Add API to get all package stats

This commit is contained in:
rubenwardy 2022-11-15 01:51:21 +00:00
parent 292b4f5483
commit 01bc519b86
3 changed files with 43 additions and 11 deletions

@ -15,6 +15,7 @@
# 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 math import math
from functools import wraps
from typing import List from typing import List
import flask_sqlalchemy import flask_sqlalchemy
@ -24,17 +25,16 @@ from sqlalchemy.orm import joinedload
from sqlalchemy.sql.expression import func from sqlalchemy.sql.expression import func
from app import csrf from app import csrf
from app.logic.graphs import get_package_stats, get_package_stats_for_user, get_all_package_stats
from app.markdown import render_markdown from app.markdown import render_markdown
from app.models import Tag, PackageState, PackageType, Package, db, PackageRelease, Permission, ForumTopic, \ from app.models import Tag, PackageState, PackageType, Package, db, PackageRelease, Permission, ForumTopic, \
MinetestRelease, APIToken, PackageScreenshot, License, ContentWarning, User, PackageReview, Thread MinetestRelease, APIToken, PackageScreenshot, License, ContentWarning, User, PackageReview, Thread
from app.querybuilder import QueryBuilder from app.querybuilder import QueryBuilder
from app.utils import is_package_page, get_int_or_abort, url_set_query, abs_url, isYes from app.utils import is_package_page, get_int_or_abort, url_set_query, abs_url, isYes
from app.logic.graphs import get_package_stats, get_package_stats_for_user
from . import bp from . import bp
from .auth import is_api_authd from .auth import is_api_authd
from .support import error, api_create_vcs_release, api_create_zip_release, api_create_screenshot, \ from .support import error, api_create_vcs_release, api_create_zip_release, api_create_screenshot, \
api_order_screenshots, api_edit_package, api_set_cover_image api_order_screenshots, api_edit_package, api_set_cover_image
from functools import wraps
def cors_allowed(f): def cors_allowed(f):
@ -441,6 +441,12 @@ def package_stats(package: Package):
return jsonify(get_package_stats(package)) return jsonify(get_package_stats(package))
@bp.route("/api/package_stats/")
@cors_allowed
def all_package_stats():
return jsonify(get_all_package_stats())
@bp.route("/api/scores/") @bp.route("/api/scores/")
@cors_allowed @cors_allowed
def package_scores(): def package_scores():

@ -112,14 +112,20 @@ Tokens can be attained by visiting [Settings > API Tokens](/user/tokens/).
* Returns daily stats for package, or null if there is no data. * Returns daily stats for package, or null if there is no data.
* Daily date is done based on the UTC timezone. * Daily date is done based on the UTC timezone.
* EXPERIMENTAL. This API may change without warning. * EXPERIMENTAL. This API may change without warning.
* A table with the following keys: * An object with the following keys:
* `from`: start date, inclusive. Ex: 2022-10-22. * `start`: start date, inclusive. Ex: 2022-10-22.
* `end`: end date, inclusive. Ex: 2022-11-05. * `end`: end date, inclusive. Ex: 2022-11-05.
* `platform_minetest`: list of integers per day. * `platform_minetest`: list of integers per day.
* `platform_other`: list of integers per day. * `platform_other`: list of integers per day.
* `reason_new`: list of integers per day. * `reason_new`: list of integers per day.
* `reason_dependency`: list of integers per day. * `reason_dependency`: list of integers per day.
* `reason_update`: list of integers per day. * `reason_update`: list of integers per day.
* GET `/api/package_stats/`
* Returns last 30 days of daily stats for _all_ packages.
* An object with the following keys:
* `start`: start date, inclusive. Ex: 2022-10-22.
* `end`: end date, inclusive. Ex: 2022-11-05.
* `package_downloads`: map from package key to list of download integers.
You can download a package by building one of the two URLs: You can download a package by building one of the two URLs:

@ -1,5 +1,7 @@
import datetime import datetime
from datetime import timedelta from datetime import timedelta
from typing import Optional
from app.models import User, Package, PackageDailyStats, db, PackageState from app.models import User, Package, PackageDailyStats, db, PackageState
from sqlalchemy import func from sqlalchemy import func
@ -68,11 +70,16 @@ def get_package_stats_for_user(user: User):
return results return results
def get_package_overview_for_user(user: User, start_date: datetime.date, end_date: datetime.date): def get_package_overview_for_user(user: Optional[User], start_date: datetime.date, end_date: datetime.date):
stats = db.session \ query = db.session \
.query(PackageDailyStats.package_id, PackageDailyStats.date, .query(PackageDailyStats.package_id, PackageDailyStats.date,
(PackageDailyStats.platform_minetest + PackageDailyStats.platform_other).label("downloads")) \ (PackageDailyStats.platform_minetest + PackageDailyStats.platform_other).label("downloads"))
.filter(PackageDailyStats.package.has(author_id=user.id, state=PackageState.APPROVED)) \
if user:
query = query.filter(PackageDailyStats.package.has(author_id=user.id))
stats = query \
.filter(PackageDailyStats.package.has(state=PackageState.APPROVED)) \
.order_by(db.asc(PackageDailyStats.package_id), db.asc(PackageDailyStats.date)) \ .order_by(db.asc(PackageDailyStats.package_id), db.asc(PackageDailyStats.date)) \
.all() .all()
@ -84,8 +91,12 @@ def get_package_overview_for_user(user: User, start_date: datetime.date, end_dat
bucket.append(stat) bucket.append(stat)
package_title_by_id = {} package_title_by_id = {}
for package in user.packages.filter_by(state=PackageState.APPROVED).all(): pkg_query = user.packages if user else Package.query
for package in pkg_query.filter_by(state=PackageState.APPROVED).all():
if user:
package_title_by_id[package.id] = package.title package_title_by_id[package.id] = package.title
else:
package_title_by_id[package.id] = package.getId()
result = {} result = {}
@ -105,5 +116,14 @@ def get_package_overview_for_user(user: User, start_date: datetime.date, end_dat
else: else:
row.append(0) row.append(0)
return result return result
def get_all_package_stats():
end_date = datetime.datetime.utcnow().date()
start_date = (datetime.datetime.utcnow() - datetime.timedelta(days=29)).date()
return {
"start": start_date.isoformat(),
"end": end_date.isoformat(),
"package_downloads": get_package_overview_for_user(None, start_date, end_date),
}