diff options
-rw-r--r-- | newspipe/templates/history.html | 134 | ||||
-rw-r--r-- | newspipe/web/views/__init__.py | 2 | ||||
-rw-r--r-- | newspipe/web/views/article.py | 7 | ||||
-rw-r--r-- | newspipe/web/views/stats.py | 18 | ||||
-rw-r--r-- | package-lock.json | 12 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rwxr-xr-x | runserver.py | 1 |
7 files changed, 152 insertions, 23 deletions
diff --git a/newspipe/templates/history.html b/newspipe/templates/history.html index 0638993c..35712ca3 100644 --- a/newspipe/templates/history.html +++ b/newspipe/templates/history.html @@ -1,26 +1,118 @@ {% extends "layout.html" %} +{% block head %} +{{ super() }} +<script src="{{ url_for('static', filename='npm_components/chart.js/dist/chart.min.js') }}"></script> +<style> + .chart-container { + display: block; + float: none; + width: 20%; + margin-top: 0px; + margin-right:0px; + margin-left:0px; + height: auto; + } +</style> +{% endblock %} {% block content %} <div class="container"> - <h1>{{ _('History') }}</h1> - {% if month != None %} - <h2><a href="{{ url_for("articles.history", year=year) }}"><span class="fa fa-chevron-left" aria-hidden="true"></span> {{ year }}</a></h2> - <h3>{{ month | month_name }}</h3> - {% elif year != None %} - <h2><a href="{{ url_for("articles.history") }}"><span class="fa fa-chevron-left" aria-hidden="true"></span> {{ _('all years') }}</a></h2> - <h3>{{ year }}</h3> - {% endif %} - <ul class="list-group"> - {% for date in articles_counter | sort(reverse = True) %} - {% if year == None %} - <li class="list-group-item"><a href="{{ url_for("articles.history", year=date) }}">{{ date }}</a> : {{ articles_counter[date] }} articles</li> - {% elif month == None %} - <li class="list-group-item"><a href="{{ url_for("articles.history", year=year, month=date) }}">{{ date | month_name }}</a> : {{ articles_counter[date] }} articles</li> - {% else %} - {% for article in articles %} - <li class="list-group-item">{{ article.date | datetime }} - <a href="/article/{{ article.id }}">{{ article.title | safe }}</a></li> - {% endfor %} - {% endif %} - {% endfor %} - </ul> + <h1>{{ _('History') }}</h1> + + {% if month != None %} + <h2><a href="{{ url_for("articles.history", year=year) }}"><span class="fa fa-chevron-left" aria-hidden="true"></span> {{ year }}</a></h2> + <h3>{{ month | month_name }}</h3> + {% elif year != None %} + <h2><a href="{{ url_for("articles.history") }}"><span class="fa fa-chevron-left" aria-hidden="true"></span> {{ _('all years') }}</a></h2> + <h3>{{ year }}</h3> + {% endif %} + + {% if month == None %} + <div class="row"> + <div class="col chart-container"> + <canvas id="stats-history"></canvas> + </div> + </div> + {% endif %} + + {% if year != None and month != None %} + <div class="row"> + <div class="col"> + <ul class="list-group"> + {% for date in articles_counter | sort(reverse = True) %} + {% for article in articles %} + <li class="list-group-item">{{ article.date | datetime }} - <a href="/article/{{ article.id }}">{{ article.title | safe }}</a></li> + {% endfor %} + {% endfor %} + </ul> + </div> + </div> + {% endif %} </div><!-- /.container --> +<script> + document.addEventListener("DOMContentLoaded", function() { + var months = [ "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" ]; + var colors = ['rgba(230, 25, 75, 0.4)', 'rgba(60, 180, 75, 0.4)', + 'rgba(255, 225, 25, 0.4)', 'rgba(0, 130, 200, 0.4)', 'rgba(245, 130, 48, 0.4)', + 'rgba(145, 30, 180, 0.4)', 'rgba(70, 240, 240, 0.4)', 'rgba(240, 50, 230, 0.4)', + 'rgba(210, 245, 60, 0.4)', 'rgba(250, 190, 190, 0.4)', 'rgba(0, 128, 128, 0.4)', + 'rgb(148, 163, 209, 0.4)', 'rgba(170, 110, 40, 0.4)', 'rgb(141, 140, 255, 0.4)', + 'rgba(128, 0, 0, 0.4)', 'rgba(170, 255, 195, 0.4)', 'rgba(128, 128, 0, 0.4)', + 'rgba(255, 215, 180, 0.4)', 'rgba(0, 0, 128, 0.4)', 'rgb(241, 147, 241, 0.4)', + 'rgba(255, 255, 255, 0.4)', 'rgb(129, 181, 255, 0.4)', 'rgb(229, 236, 202, 0.4)', + 'rgb(157, 196, 241, 0.4)', 'rgb(253, 141, 211, 0.4)', 'rgb(180, 128, 253, 0.4)', + 'rgb(255, 195, 129, 0.4)', 'rgb(204, 228, 230, 0.4)']; + + var period = window.location.pathname.split("/history")[1] + if (period == null){ + period = ""; + } + + if (period.split('/').length - 1 < 2) { + fetch("/stats/history.json" + period) + .then(response => response.json()) + .then(result => { + if (period.split('/').length - 1 == 1) { + var labels = Object.keys(result).map(function(e){return months[e-1]}); + } else { + var labels = Object.keys(result); + } + var ctx = document.getElementById("stats-history").getContext('2d'); + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: labels, + datasets: [{ + label: 'History of aggregated articles.', + data: Object.values(result), + borderWidth: 1, + backgroundColor: colors + }], + }, + options: { + responsive: true, + maintainAspectRatio: false, + onClick: function(evt) { + const points = myChart.getElementsAtEventForMode(evt, 'nearest', { intersect: true }, true); + if (points.length) { + const firstPoint = points[0]; + var label = myChart.data.labels[firstPoint.index]; + var value = myChart.data.datasets[firstPoint.datasetIndex].data[firstPoint.index]; + + if (months.includes(label)) { + the_month = months.indexOf(label)+1; + window.location = window.location + "/" + the_month; + } else { + window.location = window.location + "/" + label; + } + } + } + } + }); + }).catch((error) => { + console.error('Error:', error); + }); + } + }); +</script> {% endblock %} diff --git a/newspipe/web/views/__init__.py b/newspipe/web/views/__init__.py index 950cb5b6..bfd25e4e 100644 --- a/newspipe/web/views/__init__.py +++ b/newspipe/web/views/__init__.py @@ -7,6 +7,7 @@ from newspipe.web.views.category import categories_bp, category_bp from newspipe.web.views.feed import feed_bp, feeds_bp from newspipe.web.views.icon import icon_bp from newspipe.web.views.user import user_bp +from newspipe.web.views.stats import stats_bp __all__ = [ "home", @@ -24,4 +25,5 @@ __all__ = [ "feeds_bp", "icon_bp", "user_bp", + "stats_bp" ] diff --git a/newspipe/web/views/article.py b/newspipe/web/views/article.py index a7d4fa93..a59722e0 100644 --- a/newspipe/web/views/article.py +++ b/newspipe/web/views/article.py @@ -94,9 +94,12 @@ def delete(article_id=None): @articles_bp.route("/history/<int:year>/<int:month>", methods=["GET"]) @login_required def history(year=None, month=None): - cntr, artcles = ArticleController(current_user.id).get_history(year, month) + if month is not None: + cntr, articles = ArticleController(current_user.id).get_history(year, month) + else: + cntr, articles = {}, [] return render_template( - "history.html", articles_counter=cntr, articles=artcles, year=year, month=month + "history.html", articles_counter=cntr, articles=articles, year=year, month=month ) diff --git a/newspipe/web/views/stats.py b/newspipe/web/views/stats.py new file mode 100644 index 00000000..d890524c --- /dev/null +++ b/newspipe/web/views/stats.py @@ -0,0 +1,18 @@ + +from flask import ( + Blueprint, + jsonify +) +from newspipe.controllers import ArticleController +from flask_login import current_user, login_required + +stats_bp = Blueprint("stats", __name__, url_prefix="/stats") + + +@stats_bp.route("/history.json", methods=["GET"]) +@stats_bp.route("/history.json/<int:year>", methods=["GET"]) +@stats_bp.route("/history.json/<int:year>/<int:month>", methods=["GET"]) +@login_required +def history(year=None, month=None): + cntr, _ = ArticleController(current_user.id).get_history(year, month) + return jsonify(cntr) diff --git a/package-lock.json b/package-lock.json index bdb25d35..921a32a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "newspipe", "version": "9.1.0", "hasInstallScript": true, "license": "AGPL-3.0", @@ -12,6 +13,7 @@ "@popperjs/core": "^2.11.0", "bootstrap": "^5.1.3", "bootstrap-select": "^1.14.0-beta2", + "chart.js": "^3.7.0", "fork-awesome": "^1.2.0", "moment": "^2.29.1" }, @@ -49,6 +51,11 @@ "jquery": "1.9.1 - 3" } }, + "node_modules/chart.js": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", + "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" + }, "node_modules/fork-awesome": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fork-awesome/-/fork-awesome-1.2.0.tgz", @@ -90,6 +97,11 @@ "integrity": "sha512-Q63QUbConUwA+/Te7tCJcv0nE3SI/J+rNI5A1mdX1KxP6lW0pFQy+4KVP6VwgZEcwkoPfrwjvAo6WT7fdl+Sdg==", "requires": {} }, + "chart.js": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", + "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" + }, "fork-awesome": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fork-awesome/-/fork-awesome-1.2.0.tgz", diff --git a/package.json b/package.json index 568fe391..b0ff38f0 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "@popperjs/core": "^2.11.0", "bootstrap": "^5.1.3", "bootstrap-select": "^1.14.0-beta2", + "chart.js": "^3.7.0", "fork-awesome": "^1.2.0", "moment": "^2.29.1" }, diff --git a/runserver.py b/runserver.py index 89b8f553..32e5e8e7 100755 --- a/runserver.py +++ b/runserver.py @@ -49,6 +49,7 @@ with application.app_context(): application.register_blueprint(views.user_bp) application.register_blueprint(views.bookmarks_bp) application.register_blueprint(views.bookmark_bp) + application.register_blueprint(views.stats_bp) register_commands(application) |