Refactoring statistics api

This commit is contained in:
hok7z 2023-02-27 13:38:43 +02:00
parent 8aa85d10ed
commit ab4e560ba7
5 changed files with 212 additions and 199 deletions

15
app.py
View File

@ -2,23 +2,22 @@ from flask import Flask
import logging import logging
from apps.admin import admin as blueprint_admin from apps import statistics_app
from apps.auth import auth as blueprint_auth from apps import auth_app
from apps import admin_app
from apps.group_stat_api import group_stat_api as blueprint_stat_api
from config import secret_key from config import secret_key
app = Flask(__name__) app = Flask(__name__)
app.secret_key = secret_key app.secret_key = secret_key
app.register_blueprint(blueprint_auth) app.register_blueprint(auth_app)
app.register_blueprint(blueprint_admin) app.register_blueprint(admin_app)
app.register_blueprint(blueprint_stat_api) app.register_blueprint(statistics_app)
if __name__ == '__main__': if __name__ == '__main__':
from database import build_database from database import build_database
build_database() build_database()
logging.info("Build database models") logging.info("Build database models")
app.run(host="0.0.0.0") app.run(host="0.0.0.0")

3
apps/__init__.py Normal file
View File

@ -0,0 +1,3 @@
from .admin import admin as admin_app
from .auth import auth as auth_app
from .group_statistics_api import statistics_api as statistics_app

View File

@ -1,146 +0,0 @@
from flask import Blueprint
from flask import request, jsonify
from peewee import SQL, fn
from database import Message, Member
from datetime import datetime
group_stat_api = Blueprint('group_stat_api', __name__)
@group_stat_api.route("/top_users", methods=["GET"])
def top_users():
number = request.args.get("number")
if not number: number = 3
number = int(number)
members = []
counts = []
query = (Member
.select(fn.count(Message.id).alias("count"), Member)
.join(Message)
.group_by(Member)
.order_by(SQL("count").desc())
)
for group in query:
members.append(group.first_name)
counts.append(group.count)
members.insert(0, "Others")
counts.insert(0, sum(counts[number:]))
return jsonify({
"members": members[:number + 1],
"counts": counts[:number + 1]
})
@group_stat_api.route("/chat_activity", methods=["GET"])
def chat_activity():
dates = []
counts = []
from_date = request.args.get("from_date")
to_date = request.args.get("to_date")
if not from_date or not to_date:
return jsonify({
"err": "from_date/to_date empty"
}),422
date_format = request.args.get("date_format")
date_format = date_format if date_format else "%Y-%m-%d"
group_by = request.args.get("group_by")
group_by = group_by if group_by else "day"
try:
from_date = datetime.strptime(from_date, date_format)
to_date = datetime.strptime(to_date, date_format)
except ValueError:
return jsonify({
"from_date": from_date,
"to_date": to_date,
"date_format": date_format,
"err":"can't format datatime obj"
}),422
if not group_by in ["day","week","month","year"]:
return jsonify({
"err":"invalid group_by"
}),422
query = (Message
.select(fn.date_trunc(group_by, Message.timestamp).alias('range'), fn.count(Message.id).alias('count'))
.where((Message.timestamp >= from_date) & (Message.timestamp <= to_date))
.group_by(fn.date_trunc(group_by, Message.timestamp))
.order_by(SQL("range"))
)
for row in query:
dates.append(row.range.strftime(date_format))
counts.append(row.count)
return jsonify({
"date": dates,
"counts": counts
})
@group_stat_api.route("/user_activity", methods=["GET"])
def user_activity():
dates = []
counts = []
user_id = request.args.get("user_id")
user = Member.get_or_none(Member.user_id == user_id)
if not user:
return jsonify({
"err":"invalid user_id"
}),422
from_date = request.args.get("from_date")
to_date = request.args.get("to_date")
if not from_date or not to_date:
return jsonify({
"err": "from_date/to_date empty"
}),422
group_by = request.args.get("group_by")
group_by = group_by if group_by else "day"
date_format = request.args.get("date_format")
date_format = date_format if date_format else "%Y-%m-%d"
try:
from_date = datetime.strptime(from_date,date_format)
to_date = datetime.strptime(to_date, date_format)
except ValueError:
return jsonify({
"from_date": from_date,
"to_date": to_date,
"date_format": date_format,
"err":"can't format datatime obj"
}),422
query = (Message
.select( fn.date_trunc(group_by, Message.timestamp).alias("range"), fn.count(Message.id).alias("count") )
.where((Message.user == user) & (Message.timestamp >= from_date) & (Message.timestamp <= to_date))
.group_by(fn.date_trunc(group_by, Message.timestamp))
.order_by(SQL("range"))
)
for row in query:
dates.append(row.range.strftime(date_format))
counts.append(row.count)
return jsonify({
"date": dates,
"counts": counts
})

View File

@ -0,0 +1,157 @@
from flask import Blueprint
from flask import request, jsonify
from peewee import SQL, fn
from database import Message, Member
from datetime import datetime
statistics_api = Blueprint('statistics_api', __name__)
@statistics_api.route("/top-users", methods=["GET"])
def top_users():
"""
/top_users - top users for activity
:param number - number top users
"""
data = []
number = int(request.args.get("number"))
if not number:
number = 3
query = (Member
.select(fn.count(Message.id).alias("count"), Member)
.join(Message)
.group_by(Member)
.order_by(SQL("count").desc())
)
for group in query:
data.append({
"user": group.first_name,
"count": group.count,
})
data.insert(0, {
"user": "Others",
"count": sum(item["count"] for i, item in enumerate(data) if i >= number),
})
return jsonify(data[:number + 1])
@statistics_api.route("/last-chat-activity", methods=["GET"])
def last_chat_activity():
"""
/last-chat-activity - last chat activity selected range
:param startDate: datetime at which start(optional)
:param endDate: datetime at which end
:param groupBy: year, month, day, hour
:param formatDatetime: format string datetime object(default: %Y-%m-%d)
"""
data = []
start_date = request.args.get("startDate")
end_date = request.args.get("endDate")
if not start_date or not end_date:
return jsonify({
"err": "startDate/endDate must be defined"
}), 422
date_format = request.args.get("formatDatetime")
if not date_format:
date_format = "%Y-%m-%d"
groupby_keys = ["year", "month", "week", "day"]
group_by = request.args.get("groupBy")
if not group_by or not (group_by in groupby_keys):
return jsonify({
"err": "invalid groupBy parametr(year, month, day, hour)"
}), 422
try:
start_date = datetime.strptime(start_date, date_format)
end_date = datetime.strptime(end_date, date_format)
except ValueError:
return jsonify({
"err": "Can't unparse startDate/endDate"
}), 422
query = (Message
.select(fn.date_trunc(group_by, Message.timestamp).alias('range'), fn.count(Message.id).alias('count'))
.where((Message.timestamp >= start_date) & (Message.timestamp <= end_date))
.group_by(fn.date_trunc(group_by, Message.timestamp))
.order_by(SQL("range"))
)
for record in query:
data.append({
"datetime": record.range.strftime(date_format),
"count": record.count,
})
return jsonify(data)
@statistics_api.route("/last-user-activity", methods=["GET"])
def last_user_activity():
"""
/last-user-activity - user activity selected range
:param user_id: user id
:param startDate: datetime at which start(optional)
:param endDate: datetime at which end
:param groupBy: year, month, day, hour
:param formatDatetime: format string datetime object(default: %Y-%m-%d)
"""
data = []
user_id = request.args.get("user_id")
user = Member.get_or_none(Member.user_id == user_id)
start_date = request.args.get("startDate")
end_date = request.args.get("endDate")
if not start_date or not end_date:
return jsonify({
"err": "startDate/endDate must be defined"
}), 422
date_format = request.args.get("formatDatetime")
if not date_format:
date_format = "%Y-%m-%d"
groupby_keys = ["year", "month", "week", "day"]
group_by = request.args.get("groupBy")
if not group_by or not (group_by in groupby_keys):
return jsonify({
"err": "invalid groupBy parametr(year, month, day, hour)"
}), 422
try:
start_date = datetime.strptime(start_date, date_format)
end_date = datetime.strptime(end_date, date_format)
except ValueError:
return jsonify({
"err": "Can't unparse start_date and end_date"
}), 422
query = (Message
.select(fn.date_trunc(group_by, Message.timestamp).alias("range"), fn.count(Message.id).alias("count"))
.where((Message.user == user) & (Message.timestamp >= start_date) & (Message.timestamp <= end_date))
.group_by(fn.date_trunc(group_by, Message.timestamp))
.order_by(SQL("range"))
)
for record in query:
data.append({
"datetime": record.range.strftime(date_format),
"count": record.count,
})
return jsonify(data)

View File

@ -1,51 +1,51 @@
fetch("/top_users?number=5") fetch("/top-users?number=5")
.then(response => { .then(response => {
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`); throw new Error(`HTTP error: ${response.status}`);
} }
return response.json(); return response.json();
}).then((data) => { }).then((data) => {
const ctx = document.getElementById('top-users'); const ctx = document.getElementById('top-users');
new Chart(ctx, {
type: 'doughnut',
data: {
labels: data.map(data => data.user),
datasets: [{
data: data.map(data => data.count)
}]
},
new Chart(ctx, { plugins: [ChartDataLabels],
type: 'doughnut', options: {
data: { maintainAspectRatio: false,
labels: data["members"], plugins :{
datasets: [{ legend: false,
data: data["counts"]
}] datalabels: {
color: '#f4f6fc',
formatter: function(value, context) {
return context.chart.data.labels[context.dataIndex];
}
}, },
plugins: [ChartDataLabels], tooltip: {
options: { callbacks: {
maintainAspectRatio: false, label: (context) => {
plugins :{ let sum = 0;
legend: false, let value = context.parsed;
let dataArray = context.dataset.data;
datalabels: { dataArray.map(data => {
color: '#f4f6fc', sum += data;
formatter: function(value, context) { });
return context.chart.data.labels[context.dataIndex]; let percentage = Math.round(value*100 / sum);
}
},
tooltip: { return `${percentage}%`
callbacks: {
label: (context) => {
let sum = 0;
let value = context.parsed;
let dataArray = context.dataset.data;
dataArray.map(data => {
sum += data;
});
let percentage = Math.round(value*100 / sum);
return `${percentage}%`
}
}
}
} }
}, }
}); }
}
},
});
}) })