Added history statistics

pull/1038/head
Louis Vézina 4 years ago
parent aac4b56349
commit 1cfc193758

@ -4,6 +4,7 @@ import os
import ast import ast
from datetime import timedelta from datetime import timedelta
import datetime import datetime
from dateutil import rrule
import pretty import pretty
import time import time
from operator import itemgetter from operator import itemgetter
@ -1256,6 +1257,58 @@ class HistoryMovies(Resource):
return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data) return jsonify(draw=draw, recordsTotal=row_count, recordsFiltered=row_count, data=data)
class HistoryStats(Resource):
@authenticate
def get(self):
timeframe = request.args.get('timeframe') or 'month'
action = request.args.get('action') or 'All'
provider = request.args.get('provider') or 'All'
language = request.args.get('language') or 'All'
history_where_clause = " WHERE id"
# timeframe must be in ['week', 'month', 'trimester', 'year']
if timeframe == 'year':
days = 364
elif timeframe == 'trimester':
days = 90
elif timeframe == 'month':
days = 30
elif timeframe == 'week':
days = 6
history_where_clause += " AND datetime(timestamp, 'unixepoch') BETWEEN datetime('now', '-" + str(days) + \
" days') AND datetime('now', 'localtime')"
if action != 'All':
history_where_clause += " AND action = " + action
else:
history_where_clause += " AND action IN (1,2,3)"
if provider != 'All':
history_where_clause += " AND provider = '" + provider + "'"
if language != 'All':
history_where_clause += " AND language = '" + language + "'"
data_series = database.execute("SELECT strftime ('%Y-%m-%d',datetime(timestamp, 'unixepoch')) as date, "
"COUNT(id) as count FROM table_history" + history_where_clause +
" GROUP BY strftime ('%Y-%m-%d',datetime(timestamp, 'unixepoch'))")
data_movies = database.execute("SELECT strftime ('%Y-%m-%d',datetime(timestamp, 'unixepoch')) as date, "
"COUNT(id) as count FROM table_history_movie" + history_where_clause +
" GROUP BY strftime ('%Y-%m-%d',datetime(timestamp, 'unixepoch'))")
for dt in rrule.rrule(rrule.DAILY,
dtstart=datetime.datetime.now() - datetime.timedelta(days=days),
until=datetime.datetime.now()):
if not any(d['date'] == dt.strftime('%Y-%m-%d') for d in data_series):
data_series.append({'date': dt.strftime('%Y-%m-%d'), 'count': 0})
if not any(d['date'] == dt.strftime('%Y-%m-%d') for d in data_movies):
data_movies.append({'date': dt.strftime('%Y-%m-%d'), 'count': 0})
sorted_data_series = sorted(data_series, key=lambda i: i['date'])
sorted_data_movies = sorted(data_movies, key=lambda i: i['date'])
return jsonify(data_series=sorted_data_series, data_movies=sorted_data_movies)
class WantedSeries(Resource): class WantedSeries(Resource):
@authenticate @authenticate
def get(self): def get(self):
@ -1429,6 +1482,7 @@ api.add_resource(MovieTools, '/movie_tools')
api.add_resource(HistorySeries, '/history_series') api.add_resource(HistorySeries, '/history_series')
api.add_resource(HistoryMovies, '/history_movies') api.add_resource(HistoryMovies, '/history_movies')
api.add_resource(HistoryStats, '/history_stats')
api.add_resource(WantedSeries, '/wanted_series') api.add_resource(WantedSeries, '/wanted_series')
api.add_resource(WantedMovies, '/wanted_movies') api.add_resource(WantedMovies, '/wanted_movies')

@ -25,7 +25,8 @@ from database import database, dict_mapper
from notifier import update_notifier from notifier import update_notifier
from urllib.parse import unquote from urllib.parse import unquote
from get_languages import load_language_in_db, language_from_alpha3, language_from_alpha2, alpha2_from_alpha3 from get_languages import load_language_in_db, language_from_alpha3, language_from_alpha2, alpha2_from_alpha3, \
alpha3_from_alpha2
from flask import make_response, request, redirect, abort, render_template, Response, session, flash, url_for, \ from flask import make_response, request, redirect, abort, render_template, Response, session, flash, url_for, \
send_file, stream_with_context send_file, stream_with_context
@ -247,6 +248,32 @@ def historymovies():
return render_template('historymovies.html') return render_template('historymovies.html')
@app.route('/history/stats/')
@login_required
def historystats():
data_providers = database.execute("SELECT DISTINCT provider FROM table_history WHERE provider IS NOT null "
"UNION SELECT DISTINCT provider FROM table_history_movie WHERE provider "
"IS NOT null")
data_providers_list = []
for item in data_providers:
data_providers_list.append(item['provider'])
data_languages = database.execute("SELECT DISTINCT language FROM table_history WHERE language IS NOT null "
"AND language != '' UNION SELECT DISTINCT language FROM table_history_movie "
"WHERE language IS NOT null AND language != ''")
data_languages_list = []
for item in data_languages:
splitted_lang = item['language'].split(':')
item = {"name": language_from_alpha2(splitted_lang[0]),
"code2": splitted_lang[0],
"code3": alpha3_from_alpha2(splitted_lang[0]),
"forced": True if len(splitted_lang) > 1 else False}
data_languages_list.append(item)
return render_template('historystats.html', data_providers=data_providers_list,
data_languages=sorted(data_languages_list, key=lambda i: i['name']))
@app.route('/wanted/series/') @app.route('/wanted/series/')
@login_required @login_required
def wantedseries(): def wantedseries():

@ -0,0 +1 @@
@keyframes chartjs-render-animation{from{opacity:.99}to{opacity:1}}.chartjs-render-monitor{animation:chartjs-render-animation 1ms}.chartjs-size-monitor,.chartjs-size-monitor-expand,.chartjs-size-monitor-shrink{position:absolute;direction:ltr;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1}.chartjs-size-monitor-expand>div{position:absolute;width:1000000px;height:1000000px;left:0;top:0}.chartjs-size-monitor-shrink>div{position:absolute;width:200%;height:200%;left:0;top:0}

File diff suppressed because one or more lines are too long

@ -40,6 +40,7 @@
<link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap-select.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap-select.css') }}"/>
<link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap-slider.min.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/bootstrap-slider.min.css') }}"/>
<link rel="stylesheet" href="{{ url_for('static',filename='css/jquery.typeahead.min.css') }}"/> <link rel="stylesheet" href="{{ url_for('static',filename='css/jquery.typeahead.min.css') }}"/>
<link rel="stylesheet" href="{{ url_for('static',filename='css/Chart.min.css') }}"/>
{% endblock head_css %} {% endblock head_css %}
@ -206,6 +207,7 @@
{% if settings.general.getboolean('use_radarr') %} {% if settings.general.getboolean('use_radarr') %}
<li><a href="{{ url_for('historymovies') }}"> Movies</a></li> <li><a href="{{ url_for('historymovies') }}"> Movies</a></li>
{% endif %} {% endif %}
<li><a href="{{ url_for('historystats') }}"> Statistics</a></li>
</ul> </ul>
</li> </li>
@ -325,6 +327,7 @@
<script src="{{ url_for('static',filename='js/bootstrap-slider.min.js') }}"></script> <script src="{{ url_for('static',filename='js/bootstrap-slider.min.js') }}"></script>
<script src="{{ url_for('static',filename='moment/moment.js') }}"></script> <script src="{{ url_for('static',filename='moment/moment.js') }}"></script>
<script src="{{ url_for('static',filename='js/jquery.typeahead.min.js') }}"></script> <script src="{{ url_for('static',filename='js/jquery.typeahead.min.js') }}"></script>
<script src="{{ url_for('static',filename='js/Chart.min.js') }}"></script>
<script> <script>
{% if not request.endpoint == 'login_page' %} {% if not request.endpoint == 'login_page' %}

@ -0,0 +1,149 @@
{% extends '_main.html' %}
{% block title %}History (Statistics) - Bazarr{% endblock %}
{% block bcleft %}
{% endblock bcleft %}
{% block bcright %}
{% endblock bcright %}
{% block body %}
<canvas id="history_stats" height="125"></canvas>
<nav id="edit_bar" class="navbar fixed-bottom navbar-dark bg-dark">
<div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;">
<label for="timeframe_select">Time frame: </label>
<select class="selectpicker show-tick" id="timeframe_select" name="timeframe">
<option value="week">Last week</option>
<option value="month" selected>Last month</option>
<option value="trimester">Last trimester</option>
<option value="year">Last year</option>
</select>
</div>
</div>
<div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;">
<label for="action_select">Action: </label>
<select class="selectpicker show-tick" id="action_select" name="action">
<option value="All" selected>All</option>
<option value="1">Automatically Downloaded</option>
<option value="2">Manually Downloaded</option>
<option value="3">Upgraded</option>
</select>
</div>
</div>
<div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;">
<label for="provider_select">Provider: </label>
<select class="selectpicker show-tick" id="provider_select" name="provider"></select>
</div>
</div>
<div class="form-check form-check-inline">
<div class="form-group" style="margin-bottom: 0px;">
<label for="language_select">Language: </label>
<select class="selectpicker" id="language_select" name="language"></select>
</div>
</div>
</nav>
{% endblock body %}
{% block tail %}
<script>
$(document).ready(function () {
getEnabledLanguages();
getEnabledProviders();
var timeframe = 'month';
var action = 'All';
var provider = 'All';
var language = 'All';
var labels_series = [];
var data_series = [];
var labels_movies = [];
var data_movies = [];
var history_chart = new Chart($('#history_stats'), {
type: 'bar',
data: {
labels: labels_series,
datasets: [{
label: 'Series',
backgroundColor: '#2193b5',
data: data_series
}, {
label: 'Movies',
backgroundColor: '#ffc230',
data: data_movies
}]
},
options: {
responsive: true,
scales: {
yAxes: [{
ticks: {
precision: 0
}
}]
}
}
});
get_data();
$('.selectpicker').on('change', function() {
timeframe = $('#timeframe_select').selectpicker('val');
action = $('#action_select').selectpicker('val');
provider = $('#provider_select').selectpicker('val');
language = $('#language_select').selectpicker('val');
get_data();
})
function get_data() {
$.ajax({
dataType: "json",
url: "{{ url_for('api.historystats') }}?timeframe=" + timeframe + "&action=" + action + "&provider=" + provider + "&language=" + language,
success: function(json_data) {
labels_series = json_data.data_series.map(function(e) {
return e.date;
});
data_series = json_data.data_series.map(function(e) {
return e.count;
});
data_movies = json_data.data_movies.map(function(e) {
return e.count;
});
history_chart.data.labels = labels_series;
history_chart.data.datasets[0].data = data_series;
history_chart.data.datasets[1].data = data_movies;
history_chart.update();
}
});
}
});
function getEnabledLanguages() {
$('#language_select').append('<option value="All" selected>All</option>');
{% for item in data_languages %}
$('#language_select').append('<option value="{{item['code2'] + (':forced' if item['forced'] else '')}}">{{item['name'] + (' Forced' if item['forced'] else '')}}</option>');
{% endfor %}
$("#language_select").selectpicker("refresh");
}
function getEnabledProviders() {
$('#provider_select').append('<option value="All" selected>All</option>');
{% for item in data_providers %}
$('#provider_select').append('<option value="{{item}}">{{item}}</option>');
{% endfor %}
$("#provider_select").selectpicker("refresh");
}
</script>
{% endblock tail %}
Loading…
Cancel
Save