mirror of
https://github.com/morpheus65535/bazarr
synced 2025-02-22 22:10:42 +00:00
Added history statistics
This commit is contained in:
parent
aac4b56349
commit
1cfc193758
6 changed files with 242 additions and 1 deletions
|
@ -4,6 +4,7 @@ import os
|
|||
import ast
|
||||
from datetime import timedelta
|
||||
import datetime
|
||||
from dateutil import rrule
|
||||
import pretty
|
||||
import time
|
||||
from operator import itemgetter
|
||||
|
@ -1256,6 +1257,58 @@ class HistoryMovies(Resource):
|
|||
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):
|
||||
@authenticate
|
||||
def get(self):
|
||||
|
@ -1429,6 +1482,7 @@ api.add_resource(MovieTools, '/movie_tools')
|
|||
|
||||
api.add_resource(HistorySeries, '/history_series')
|
||||
api.add_resource(HistoryMovies, '/history_movies')
|
||||
api.add_resource(HistoryStats, '/history_stats')
|
||||
|
||||
api.add_resource(WantedSeries, '/wanted_series')
|
||||
api.add_resource(WantedMovies, '/wanted_movies')
|
||||
|
|
|
@ -25,7 +25,8 @@ from database import database, dict_mapper
|
|||
from notifier import update_notifier
|
||||
|
||||
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, \
|
||||
send_file, stream_with_context
|
||||
|
||||
|
@ -247,6 +248,32 @@ def historymovies():
|
|||
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/')
|
||||
@login_required
|
||||
def wantedseries():
|
||||
|
|
1
static/css/Chart.min.css
vendored
Normal file
1
static/css/Chart.min.css
vendored
Normal file
|
@ -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}
|
7
static/js/Chart.min.js
vendored
Normal file
7
static/js/Chart.min.js
vendored
Normal file
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-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/Chart.min.css') }}"/>
|
||||
|
||||
{% endblock head_css %}
|
||||
|
||||
|
@ -206,6 +207,7 @@
|
|||
{% if settings.general.getboolean('use_radarr') %}
|
||||
<li><a href="{{ url_for('historymovies') }}"> Movies</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{{ url_for('historystats') }}"> Statistics</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -325,6 +327,7 @@
|
|||
<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='js/jquery.typeahead.min.js') }}"></script>
|
||||
<script src="{{ url_for('static',filename='js/Chart.min.js') }}"></script>
|
||||
|
||||
<script>
|
||||
{% if not request.endpoint == 'login_page' %}
|
||||
|
|
149
views/historystats.html
Normal file
149
views/historystats.html
Normal file
|
@ -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…
Reference in a new issue