mirror of
https://github.com/minetest/contentdb.git
synced 2024-12-22 22:12:24 +01:00
Add statistics page
This commit is contained in:
parent
5c0480b39d
commit
4387e71417
@ -52,6 +52,11 @@ def get_package_tabs(user: User, package: Package):
|
||||
"title": gettext("Audit Log"),
|
||||
"url": package.getURL("packages.audit")
|
||||
},
|
||||
{
|
||||
"id": "stats",
|
||||
"title": gettext("Statistics"),
|
||||
"url": package.getURL("packages.stats")
|
||||
},
|
||||
{
|
||||
"id": "share",
|
||||
"title": gettext("Share and Badges"),
|
||||
|
@ -708,3 +708,10 @@ def game_support(package):
|
||||
return render_template("packages/game_support.html", package=package, form=form,
|
||||
mod_conf_lines=mod_conf_lines, force_game_detection=force_game_detection,
|
||||
tabs=get_package_tabs(current_user, package), current_tab="game_support")
|
||||
|
||||
|
||||
@bp.route("/packages/<author>/<name>/stats/")
|
||||
@is_package_page
|
||||
def stats(package):
|
||||
return render_template("packages/stats.html",
|
||||
package=package, tabs=get_package_tabs(current_user, package), current_tab="stats")
|
||||
|
13
app/public/static/libs/chart.min.js
vendored
Normal file
13
app/public/static/libs/chart.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
app/public/static/libs/chartjs-adapter-date-fns.bundle.min.js
vendored
Normal file
7
app/public/static/libs/chartjs-adapter-date-fns.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
185
app/public/static/package_stats.js
Normal file
185
app/public/static/package_stats.js
Normal file
@ -0,0 +1,185 @@
|
||||
"use strict";
|
||||
|
||||
|
||||
const labelColor = "#bbb";
|
||||
const gridColor = "#333";
|
||||
|
||||
|
||||
const chartColors = [
|
||||
"#7eb26d",
|
||||
"#eab839",
|
||||
"#6ed0e0",
|
||||
"#e24d42",
|
||||
"#1f78c1",
|
||||
"#ba43a9",
|
||||
];
|
||||
|
||||
|
||||
function hexToRgb(hex) {
|
||||
var bigint = parseInt(hex, 16);
|
||||
var r = (bigint >> 16) & 255;
|
||||
var g = (bigint >> 8) & 255;
|
||||
var b = bigint & 255;
|
||||
|
||||
return r + "," + g + "," + b;
|
||||
}
|
||||
|
||||
|
||||
const chartColorsBg = chartColors.map(color => `rgba(${hexToRgb(color.slice(1))}, 0.2)`);
|
||||
|
||||
|
||||
async function load_data() {
|
||||
const root = document.getElementById("stats-root");
|
||||
const source = root.getAttribute("data-source");
|
||||
const response = await fetch(source);
|
||||
const json = await response.json();
|
||||
|
||||
const jsonOther = json.platform_minetest.map((value, i) =>
|
||||
value + json.platform_other[i]
|
||||
- json.reason_new[i] - json.reason_dependency[i]
|
||||
- json.reason_update[i]);
|
||||
|
||||
document.getElementById("loading").style.display = "none";
|
||||
|
||||
function getData(list) {
|
||||
return list.map((value, i) => ({ x: json.dates[i], y: value }));
|
||||
}
|
||||
|
||||
function sum(list) {
|
||||
return list.reduce((acc, x) => acc + x, 0);
|
||||
}
|
||||
|
||||
{
|
||||
const ctx = document.getElementById("chart-platform").getContext("2d");
|
||||
const data = {
|
||||
datasets: [
|
||||
{ label: "Web / other", data: getData(json.platform_other) },
|
||||
{ label: "Minetest", data: getData(json.platform_minetest) },
|
||||
],
|
||||
};
|
||||
setup_chart(ctx, data);
|
||||
}
|
||||
|
||||
{
|
||||
const ctx = document.getElementById("chart-reason").getContext("2d");
|
||||
const data = {
|
||||
datasets: [
|
||||
{ label: "Other / Unknown", data: getData(jsonOther) },
|
||||
{ label: "Update", data: getData(json.reason_update) },
|
||||
{ label: "Dependency", data: getData(json.reason_dependency) },
|
||||
{ label: "New Install", data: getData(json.reason_new) },
|
||||
],
|
||||
};
|
||||
setup_chart(ctx, data);
|
||||
}
|
||||
|
||||
{
|
||||
const ctx = document.getElementById("chart-reason-pie").getContext("2d");
|
||||
const data = {
|
||||
labels: [
|
||||
"New Install",
|
||||
"Dependency",
|
||||
"Update",
|
||||
"Other / Unknown",
|
||||
],
|
||||
datasets: [{
|
||||
label: "My First Dataset",
|
||||
data: [
|
||||
sum(json.reason_new),
|
||||
sum(json.reason_dependency),
|
||||
sum(json.reason_update),
|
||||
sum(jsonOther),
|
||||
],
|
||||
backgroundColor: chartColors,
|
||||
hoverOffset: 4,
|
||||
borderWidth: 0,
|
||||
}]
|
||||
};
|
||||
const config = {
|
||||
type: "doughnut",
|
||||
data: data,
|
||||
options: {
|
||||
plugins: {
|
||||
legend: {
|
||||
position: "right",
|
||||
labels: {
|
||||
color: labelColor,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
new Chart(ctx, config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setup_chart(ctx, data) {
|
||||
data.datasets = data.datasets.map((set, i) => {
|
||||
const colorIdx = data.datasets.length - i - 1;
|
||||
return {
|
||||
fill: true,
|
||||
backgroundColor: chartColorsBg[colorIdx],
|
||||
borderColor: chartColors[colorIdx],
|
||||
pointBackgroundColor: chartColors[colorIdx],
|
||||
...set,
|
||||
};
|
||||
});
|
||||
|
||||
const config = {
|
||||
type: "line",
|
||||
data: data,
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
tooltip: {
|
||||
mode: "index"
|
||||
},
|
||||
|
||||
legend: {
|
||||
reverse: true,
|
||||
labels: {
|
||||
color: labelColor,
|
||||
}
|
||||
}
|
||||
},
|
||||
interaction: {
|
||||
mode: "nearest",
|
||||
axis: "x",
|
||||
intersect: false
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
time: {
|
||||
// min: start,
|
||||
// max: end,
|
||||
unit: "day",
|
||||
},
|
||||
ticks: {
|
||||
color: labelColor,
|
||||
},
|
||||
grid: {
|
||||
color: gridColor,
|
||||
}
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
min: 0,
|
||||
precision: 0,
|
||||
ticks: {
|
||||
color: labelColor,
|
||||
},
|
||||
grid: {
|
||||
color: gridColor,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Chart(ctx, config);
|
||||
}
|
||||
|
||||
|
||||
$(load_data);
|
@ -272,3 +272,8 @@ blockquote {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/libs/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="/static/custom.css?v=35">
|
||||
<link rel="stylesheet" type="text/css" href="/static/custom.css?v=36">
|
||||
<link rel="search" type="application/opensearchdescription+xml" href="/static/opensearch.xml" title="ContentDB" />
|
||||
<link rel="shortcut icon" href="/favicon-16.png" sizes="16x16">
|
||||
<link rel="icon" href="/favicon-128.png" sizes="128x128">
|
||||
|
54
app/templates/packages/stats.html
Normal file
54
app/templates/packages/stats.html
Normal file
@ -0,0 +1,54 @@
|
||||
{% extends "packages/package_base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{{ _("Statistics") }}
|
||||
{% endblock %}
|
||||
|
||||
{% block scriptextra %}
|
||||
<script src="/static/libs/chart.min.js"></script>
|
||||
<script src="/static/libs/chartjs-adapter-date-fns.bundle.min.js"></script>
|
||||
<script src="/static/package_stats.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2 class="mt-0">{{ self.title() }}</h2>
|
||||
<noscript>
|
||||
<p class="alert alert-danger">
|
||||
{{ _("JavaScript is needed for graphs") }}
|
||||
</p>
|
||||
</noscript>
|
||||
<div class="row mb-5">
|
||||
<div class="col-md-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body media align-items-center">
|
||||
<i class="fas fa-download ml-2 mr-4 text-size" style="font-size: 45px; color: #999;"></i>
|
||||
<div class="media-body">
|
||||
<div class="mt-0 h4">
|
||||
{{ package.downloads }}
|
||||
</div>
|
||||
<div class="my-0">
|
||||
{{ _("Lifetime downloads") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="loading">{{ _("Loading...") }}</div>
|
||||
<div id="stats-root" data-source="{{ package.getURL('api.package_stats') }}">
|
||||
<h3>{{ _("Downloads") }}</h3>
|
||||
|
||||
<h4>{{ _("Client") }}</h4>
|
||||
<p class="text-muted">
|
||||
{{ _("This is a stacked area graph. For total downloads, look at the combined height.") }}
|
||||
</p>
|
||||
<canvas id="chart-platform" class="chart"></canvas>
|
||||
|
||||
<h4 class="mt-5">{{ _("Reason") }}</h4>
|
||||
<p class="text-muted">
|
||||
{{ _("This is a stacked area graph. For total downloads, look at the combined height.") }}
|
||||
</p>
|
||||
<canvas id="chart-reason" class="chart"></canvas>
|
||||
<canvas id="chart-reason-pie" class="chart mt-4"></canvas>
|
||||
</div>
|
||||
{% endblock %}
|
@ -164,8 +164,7 @@
|
||||
</span>
|
||||
</a>
|
||||
{% if release %}
|
||||
<a class="btn" rel="nofollow" href="{{ package.getURL("packages.download") }}" title="{{ _("Downloads") }}"
|
||||
download="{{ release.getDownloadFileName() }}">
|
||||
<a class="btn" rel="nofollow" href="{{ package.getURL('packages.stats') }}" title="{{ _('Statistics') }}">
|
||||
<i class="fas fa-download"></i>
|
||||
<span class="count">{{ package.downloads }}</span>
|
||||
</a>
|
||||
@ -544,16 +543,19 @@
|
||||
</div>
|
||||
|
||||
<p class="mt-3">
|
||||
<a href="{{ package.getURL('packages.stats') }}">
|
||||
<i class="fas fa-chart-line mr-1"></i>
|
||||
{{ _("Statistics") }}
|
||||
</a>
|
||||
{% if package.approved and current_user != package.author %}
|
||||
|
|
||||
<a href="{{ url_for('report.report', url=url_current()) }}">
|
||||
<i class="fas fa-flag mr-1"></i>
|
||||
{{ _("Report") }}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if package.checkPerm(current_user, "EDIT_PACKAGE") or package.checkPerm(current_user, "APPROVE_NEW") %}
|
||||
{% if package.approved and current_user != package.author %}
|
||||
|
|
||||
{% endif %}
|
||||
<a href="{{ package.getURL('packages.audit') }}">
|
||||
{{ _("See audit log") }}
|
||||
</a>
|
||||
|
@ -82,7 +82,6 @@ def url_set_query(**kwargs):
|
||||
else:
|
||||
args.setlist(key, [ value ])
|
||||
|
||||
|
||||
dargs = dict(args.lists())
|
||||
if request.view_args:
|
||||
dargs.update(request.view_args)
|
||||
|
Loading…
Reference in New Issue
Block a user