mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-10 03:53:26 -08:00
[Server] Serve report aggregates through the database interface (#7845)
cmdReportStats and cmdReportUserInfo still ran their queries inline through Servatrice_DatabaseInterface and built the response in the socket handler. Add ReportStatsRecord, ReportUserInfoRecord and their count records to Server_DatabaseInterface with fail-closed defaults, implement getReportStats and getReportUserInfo with the previous statements and query order, and keep presence flags for the fields the old code set only on a successful query so the proto2 wire output is unchanged. The stats cache decision and every response code stay in the handler.
This commit is contained in:
parent
2ba80cb61d
commit
a54e13756c
4 changed files with 309 additions and 144 deletions
|
|
@ -424,6 +424,66 @@ public:
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
/** @brief One report category and how many reports it covers. */
|
||||||
|
struct ReportCategoryCountRecord
|
||||||
|
{
|
||||||
|
QString category;
|
||||||
|
int count = 0;
|
||||||
|
};
|
||||||
|
/** @brief One reported or reporting user and how many reports involve them. */
|
||||||
|
struct ReportTopUserRecord
|
||||||
|
{
|
||||||
|
QString userName;
|
||||||
|
int count = 0;
|
||||||
|
};
|
||||||
|
/** @brief Aggregated report queue statistics for the moderator overview. */
|
||||||
|
struct ReportStatsRecord
|
||||||
|
{
|
||||||
|
int totalReports = 0;
|
||||||
|
int totalPending = 0;
|
||||||
|
int totalAssigned = 0;
|
||||||
|
int totalResolved = 0;
|
||||||
|
int reportsLast24h = 0;
|
||||||
|
int reportsLast7d = 0;
|
||||||
|
int reportsLast30d = 0;
|
||||||
|
int reportsThisWeek = 0;
|
||||||
|
bool hasReportsLastWeek = false;
|
||||||
|
int reportsLastWeek = 0;
|
||||||
|
bool hasAvgResolutionHours = false;
|
||||||
|
double avgResolutionHours = 0.0;
|
||||||
|
QList<ReportCategoryCountRecord> categoryCounts;
|
||||||
|
QList<ReportTopUserRecord> topReportedUsers;
|
||||||
|
QList<ReportTopUserRecord> topReporters;
|
||||||
|
/// False when a non-overview query failed, which keeps the result out of the cache.
|
||||||
|
bool allQueriesOk = true;
|
||||||
|
};
|
||||||
|
virtual bool getReportStats(ReportStatsRecord & /* stats */)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/** @brief Report history and moderation counts for one user, for the moderator overview. */
|
||||||
|
struct ReportUserInfoRecord
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
bool isAdmin = false;
|
||||||
|
bool isActive = false;
|
||||||
|
qint64 registrationTime = 0;
|
||||||
|
bool hasAdminNotes = false;
|
||||||
|
QString adminNotes;
|
||||||
|
bool hasTotalReports = false;
|
||||||
|
int totalReports = 0;
|
||||||
|
bool hasTotalBans = false;
|
||||||
|
int totalBans = 0;
|
||||||
|
bool hasTotalWarns = false;
|
||||||
|
int totalWarns = 0;
|
||||||
|
bool hasLastLogin = false;
|
||||||
|
qint64 lastLogin = 0;
|
||||||
|
QList<ServerInfo_Report> recentReports;
|
||||||
|
};
|
||||||
|
virtual bool getReportUserInfo(const QString & /* userName */, ReportUserInfoRecord & /* info */)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
/** @brief One resolved or dismissed report whose reporter has not been told yet. */
|
/** @brief One resolved or dismissed report whose reporter has not been told yet. */
|
||||||
struct PendingReportNotification
|
struct PendingReportNotification
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -3843,6 +3843,204 @@ bool Servatrice_DatabaseInterface::getReportComments(int reportId, QList<ServerI
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Servatrice_DatabaseInterface::getReportStats(ReportStatsRecord &stats)
|
||||||
|
{
|
||||||
|
if (!checkSql()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *overviewQuery =
|
||||||
|
prepareQuery("SELECT COUNT(*) AS total, "
|
||||||
|
"SUM(status IN ('open', 'assigned')) AS pending, "
|
||||||
|
"SUM(status = 'assigned') AS assigned, "
|
||||||
|
"SUM(status IN ('resolved', 'dismissed')) AS resolved, "
|
||||||
|
"SUM(created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)) AS last24h, "
|
||||||
|
"SUM(created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)) AS last7d, "
|
||||||
|
"SUM(created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)) AS last30d, "
|
||||||
|
"SUM(created_at >= DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)) AS this_week "
|
||||||
|
"FROM {prefix}_reports");
|
||||||
|
if (!execSqlQuery(overviewQuery) || !overviewQuery->next()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.totalReports = overviewQuery->value(0).toInt();
|
||||||
|
stats.totalPending = overviewQuery->value(1).toInt();
|
||||||
|
stats.totalAssigned = overviewQuery->value(2).toInt();
|
||||||
|
stats.totalResolved = overviewQuery->value(3).toInt();
|
||||||
|
stats.reportsLast24h = overviewQuery->value(4).toInt();
|
||||||
|
stats.reportsLast7d = overviewQuery->value(5).toInt();
|
||||||
|
stats.reportsLast30d = overviewQuery->value(6).toInt();
|
||||||
|
stats.reportsThisWeek = overviewQuery->value(7).toInt();
|
||||||
|
|
||||||
|
stats.allQueriesOk = true;
|
||||||
|
|
||||||
|
QSqlQuery *lastWeekQuery =
|
||||||
|
prepareQuery("SELECT COUNT(*) FROM {prefix}_reports WHERE created_at >= DATE_SUB(CURDATE(), "
|
||||||
|
"INTERVAL WEEKDAY(CURDATE()) + 7 DAY) "
|
||||||
|
"AND created_at < DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)");
|
||||||
|
if (execSqlQuery(lastWeekQuery) && lastWeekQuery->next()) {
|
||||||
|
stats.hasReportsLastWeek = true;
|
||||||
|
stats.reportsLastWeek = lastWeekQuery->value(0).toInt();
|
||||||
|
} else {
|
||||||
|
stats.allQueriesOk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *avgResQuery =
|
||||||
|
prepareQuery("SELECT AVG(TIMESTAMPDIFF(HOUR, created_at, resolution_time)) "
|
||||||
|
"FROM {prefix}_reports WHERE status IN ('resolved','dismissed') AND resolution_time IS NOT NULL");
|
||||||
|
if (execSqlQuery(avgResQuery) && avgResQuery->next()) {
|
||||||
|
if (!avgResQuery->value(0).isNull()) {
|
||||||
|
stats.hasAvgResolutionHours = true;
|
||||||
|
stats.avgResolutionHours = avgResQuery->value(0).toDouble();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.allQueriesOk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *catQuery =
|
||||||
|
prepareQuery("SELECT category, COUNT(*) AS cnt FROM {prefix}_reports GROUP BY category ORDER BY cnt DESC "
|
||||||
|
"LIMIT 10");
|
||||||
|
if (execSqlQuery(catQuery)) {
|
||||||
|
while (catQuery->next()) {
|
||||||
|
ReportCategoryCountRecord record;
|
||||||
|
record.category = catQuery->value(0).toString();
|
||||||
|
record.count = catQuery->value(1).toInt();
|
||||||
|
stats.categoryCounts.append(record);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.allQueriesOk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *topReportedQuery = prepareQuery("SELECT reported_user_name, COUNT(*) AS cnt FROM {prefix}_reports "
|
||||||
|
"GROUP BY reported_user_name ORDER BY cnt DESC LIMIT 10");
|
||||||
|
if (execSqlQuery(topReportedQuery)) {
|
||||||
|
while (topReportedQuery->next()) {
|
||||||
|
ReportTopUserRecord record;
|
||||||
|
record.userName = topReportedQuery->value(0).toString();
|
||||||
|
record.count = topReportedQuery->value(1).toInt();
|
||||||
|
stats.topReportedUsers.append(record);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.allQueriesOk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *topReporterQuery = prepareQuery("SELECT reporter_name, COUNT(*) AS cnt FROM {prefix}_reports "
|
||||||
|
"GROUP BY reporter_name ORDER BY cnt DESC LIMIT 10");
|
||||||
|
if (execSqlQuery(topReporterQuery)) {
|
||||||
|
while (topReporterQuery->next()) {
|
||||||
|
ReportTopUserRecord record;
|
||||||
|
record.userName = topReporterQuery->value(0).toString();
|
||||||
|
record.count = topReporterQuery->value(1).toInt();
|
||||||
|
stats.topReporters.append(record);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stats.allQueriesOk = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Servatrice_DatabaseInterface::getReportUserInfo(const QString &userName, ReportUserInfoRecord &info)
|
||||||
|
{
|
||||||
|
if (!checkSql()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *userQuery = prepareQuery("SELECT u.admin, u.active, u.registrationDate, u.adminnotes "
|
||||||
|
"FROM {prefix}_users u "
|
||||||
|
"WHERE u.name = :name");
|
||||||
|
userQuery->bindValue(":name", userName);
|
||||||
|
|
||||||
|
if (!execSqlQuery(userQuery)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userQuery->next()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.found = true;
|
||||||
|
info.isAdmin = userQuery->value(0).toBool();
|
||||||
|
info.isActive = userQuery->value(1).toBool();
|
||||||
|
info.registrationTime = userQuery->value(2).toDateTime().toSecsSinceEpoch();
|
||||||
|
|
||||||
|
if (!userQuery->value(3).isNull()) {
|
||||||
|
info.hasAdminNotes = true;
|
||||||
|
info.adminNotes = userQuery->value(3).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *reportCountQuery =
|
||||||
|
prepareQuery("SELECT COUNT(*) FROM {prefix}_reports WHERE reported_user_name = :name");
|
||||||
|
reportCountQuery->bindValue(":name", userName);
|
||||||
|
if (execSqlQuery(reportCountQuery) && reportCountQuery->next()) {
|
||||||
|
info.hasTotalReports = true;
|
||||||
|
info.totalReports = reportCountQuery->value(0).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *banCountQuery = prepareQuery("SELECT COUNT(*) FROM {prefix}_bans WHERE user_name = :name");
|
||||||
|
banCountQuery->bindValue(":name", userName);
|
||||||
|
if (execSqlQuery(banCountQuery) && banCountQuery->next()) {
|
||||||
|
info.hasTotalBans = true;
|
||||||
|
info.totalBans = banCountQuery->value(0).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *warnCountQuery = prepareQuery("SELECT COUNT(*) FROM {prefix}_warnings WHERE user_name = :name");
|
||||||
|
warnCountQuery->bindValue(":name", userName);
|
||||||
|
if (execSqlQuery(warnCountQuery) && warnCountQuery->next()) {
|
||||||
|
info.hasTotalWarns = true;
|
||||||
|
info.totalWarns = warnCountQuery->value(0).toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *lastLoginQuery = prepareQuery("SELECT UNIX_TIMESTAMP(a.last_login) "
|
||||||
|
"FROM {prefix}_user_analytics a "
|
||||||
|
"JOIN {prefix}_users u ON u.id = a.id "
|
||||||
|
"WHERE u.name = :name");
|
||||||
|
lastLoginQuery->bindValue(":name", userName);
|
||||||
|
if (execSqlQuery(lastLoginQuery) && lastLoginQuery->next() && !lastLoginQuery->value(0).isNull()) {
|
||||||
|
info.hasLastLogin = true;
|
||||||
|
info.lastLogin = lastLoginQuery->value(0).toLongLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSqlQuery *recentQuery = prepareQuery("SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, "
|
||||||
|
"r.category, r.description, r.created_at, r.status, "
|
||||||
|
"r.resolution_note, ru.name AS assigned_mod_name "
|
||||||
|
"FROM {prefix}_reports r "
|
||||||
|
"LEFT JOIN {prefix}_users ru ON r.assigned_to = ru.id "
|
||||||
|
"WHERE r.reported_user_name = :name "
|
||||||
|
"ORDER BY r.created_at DESC LIMIT 10");
|
||||||
|
recentQuery->bindValue(":name", userName);
|
||||||
|
|
||||||
|
if (execSqlQuery(recentQuery)) {
|
||||||
|
while (recentQuery->next()) {
|
||||||
|
ServerInfo_Report report;
|
||||||
|
report.set_report_id(recentQuery->value(0).toInt());
|
||||||
|
report.set_reporter_name(recentQuery->value(1).toString().toStdString());
|
||||||
|
report.set_reported_user_name(recentQuery->value(2).toString().toStdString());
|
||||||
|
|
||||||
|
if (!recentQuery->value(3).isNull()) {
|
||||||
|
report.set_game_id(recentQuery->value(3).toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
report.set_category(recentQuery->value(4).toString().toStdString());
|
||||||
|
report.set_description(recentQuery->value(5).toString().toStdString());
|
||||||
|
report.set_report_time(recentQuery->value(6).toDateTime().toSecsSinceEpoch());
|
||||||
|
report.set_status(recentQuery->value(7).toString().toStdString());
|
||||||
|
|
||||||
|
if (!recentQuery->value(8).isNull()) {
|
||||||
|
report.set_resolution_note(recentQuery->value(8).toString().toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recentQuery->value(9).isNull()) {
|
||||||
|
report.set_assigned_mod_name(recentQuery->value(9).toString().toStdString());
|
||||||
|
}
|
||||||
|
|
||||||
|
info.recentReports.append(report);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int Servatrice_DatabaseInterface::countReportsSubmittedInLastDay(int reporterId)
|
int Servatrice_DatabaseInterface::countReportsSubmittedInLastDay(int reporterId)
|
||||||
{
|
{
|
||||||
if (!checkSql()) {
|
if (!checkSql()) {
|
||||||
|
|
|
||||||
|
|
@ -301,6 +301,8 @@ public:
|
||||||
Server_DatabaseInterface::ReportLookupResult
|
Server_DatabaseInterface::ReportLookupResult
|
||||||
getReportForDetails(int reportId, int &reporterId, ServerInfo_Report &report) override;
|
getReportForDetails(int reportId, int &reporterId, ServerInfo_Report &report) override;
|
||||||
bool getReportComments(int reportId, QList<ServerInfo_ReportComment> &comments) override;
|
bool getReportComments(int reportId, QList<ServerInfo_ReportComment> &comments) override;
|
||||||
|
bool getReportStats(ReportStatsRecord &stats) override;
|
||||||
|
bool getReportUserInfo(const QString &userName, ReportUserInfoRecord &info) override;
|
||||||
bool addReport(int reporterId,
|
bool addReport(int reporterId,
|
||||||
const QString &reporterName,
|
const QString &reporterName,
|
||||||
int reportedUserId,
|
int reportedUserId,
|
||||||
|
|
|
||||||
|
|
@ -1992,99 +1992,50 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportResolve(const Com
|
||||||
Response::ResponseCode AbstractServerSocketInterface::cmdReportUserInfo(const Command_ReportUserInfo &cmd,
|
Response::ResponseCode AbstractServerSocketInterface::cmdReportUserInfo(const Command_ReportUserInfo &cmd,
|
||||||
ResponseContainer &rc)
|
ResponseContainer &rc)
|
||||||
{
|
{
|
||||||
if (!sqlInterface->checkSql()) {
|
if (!databaseInterface->checkSql()) {
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString userName = nameFromStdString(cmd.user_name());
|
const QString userName = nameFromStdString(cmd.user_name());
|
||||||
|
|
||||||
QSqlQuery *userQuery = sqlInterface->prepareQuery("SELECT u.admin, u.active, u.registrationDate, u.adminnotes "
|
Server_DatabaseInterface::ReportUserInfoRecord userReportInfo;
|
||||||
"FROM {prefix}_users u "
|
if (!databaseInterface->getReportUserInfo(userName, userReportInfo)) {
|
||||||
"WHERE u.name = :name");
|
|
||||||
userQuery->bindValue(":name", userName);
|
|
||||||
|
|
||||||
if (!sqlInterface->execSqlQuery(userQuery)) {
|
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userQuery->next()) {
|
if (!userReportInfo.found) {
|
||||||
return Response::RespNameNotFound;
|
return Response::RespNameNotFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
Response_ReportUserInfo *re = new Response_ReportUserInfo;
|
Response_ReportUserInfo *re = new Response_ReportUserInfo;
|
||||||
re->set_user_name(cmd.user_name());
|
re->set_user_name(cmd.user_name());
|
||||||
|
|
||||||
re->set_is_admin(userQuery->value(0).toBool());
|
re->set_is_admin(userReportInfo.isAdmin);
|
||||||
re->set_is_active(userQuery->value(1).toBool());
|
re->set_is_active(userReportInfo.isActive);
|
||||||
re->set_registration_time(userQuery->value(2).toDateTime().toSecsSinceEpoch());
|
re->set_registration_time(userReportInfo.registrationTime);
|
||||||
|
|
||||||
if (!userQuery->value(3).isNull()) {
|
if (userReportInfo.hasAdminNotes) {
|
||||||
re->set_admin_notes(userQuery->value(3).toString().toStdString());
|
re->set_admin_notes(userReportInfo.adminNotes.toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *reportCountQuery =
|
if (userReportInfo.hasTotalReports) {
|
||||||
sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_reports WHERE reported_user_name = :name");
|
re->set_total_reports(userReportInfo.totalReports);
|
||||||
reportCountQuery->bindValue(":name", userName);
|
|
||||||
if (sqlInterface->execSqlQuery(reportCountQuery) && reportCountQuery->next()) {
|
|
||||||
re->set_total_reports(reportCountQuery->value(0).toInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *banCountQuery = sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_bans WHERE user_name = :name");
|
if (userReportInfo.hasTotalBans) {
|
||||||
banCountQuery->bindValue(":name", userName);
|
re->set_total_bans(userReportInfo.totalBans);
|
||||||
if (sqlInterface->execSqlQuery(banCountQuery) && banCountQuery->next()) {
|
|
||||||
re->set_total_bans(banCountQuery->value(0).toInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *warnCountQuery =
|
if (userReportInfo.hasTotalWarns) {
|
||||||
sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_warnings WHERE user_name = :name");
|
re->set_total_warns(userReportInfo.totalWarns);
|
||||||
warnCountQuery->bindValue(":name", userName);
|
|
||||||
if (sqlInterface->execSqlQuery(warnCountQuery) && warnCountQuery->next()) {
|
|
||||||
re->set_total_warns(warnCountQuery->value(0).toInt());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *lastLoginQuery = sqlInterface->prepareQuery("SELECT UNIX_TIMESTAMP(a.last_login) "
|
if (userReportInfo.hasLastLogin) {
|
||||||
"FROM {prefix}_user_analytics a "
|
re->set_last_login(userReportInfo.lastLogin);
|
||||||
"JOIN {prefix}_users u ON u.id = a.id "
|
|
||||||
"WHERE u.name = :name");
|
|
||||||
lastLoginQuery->bindValue(":name", userName);
|
|
||||||
if (sqlInterface->execSqlQuery(lastLoginQuery) && lastLoginQuery->next() && !lastLoginQuery->value(0).isNull()) {
|
|
||||||
re->set_last_login(lastLoginQuery->value(0).toLongLong());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *recentQuery =
|
for (const ServerInfo_Report &report : userReportInfo.recentReports) {
|
||||||
sqlInterface->prepareQuery("SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, "
|
re->add_recent_reports()->CopyFrom(report);
|
||||||
"r.category, r.description, r.created_at, r.status, "
|
|
||||||
"r.resolution_note, ru.name AS assigned_mod_name "
|
|
||||||
"FROM {prefix}_reports r "
|
|
||||||
"LEFT JOIN {prefix}_users ru ON r.assigned_to = ru.id "
|
|
||||||
"WHERE r.reported_user_name = :name "
|
|
||||||
"ORDER BY r.created_at DESC LIMIT 10");
|
|
||||||
recentQuery->bindValue(":name", userName);
|
|
||||||
|
|
||||||
if (sqlInterface->execSqlQuery(recentQuery)) {
|
|
||||||
while (recentQuery->next()) {
|
|
||||||
ServerInfo_Report *info = re->add_recent_reports();
|
|
||||||
info->set_report_id(recentQuery->value(0).toInt());
|
|
||||||
info->set_reporter_name(recentQuery->value(1).toString().toStdString());
|
|
||||||
info->set_reported_user_name(recentQuery->value(2).toString().toStdString());
|
|
||||||
|
|
||||||
if (!recentQuery->value(3).isNull()) {
|
|
||||||
info->set_game_id(recentQuery->value(3).toInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
info->set_category(recentQuery->value(4).toString().toStdString());
|
|
||||||
info->set_description(recentQuery->value(5).toString().toStdString());
|
|
||||||
info->set_report_time(recentQuery->value(6).toDateTime().toSecsSinceEpoch());
|
|
||||||
info->set_status(recentQuery->value(7).toString().toStdString());
|
|
||||||
|
|
||||||
if (!recentQuery->value(8).isNull()) {
|
|
||||||
info->set_resolution_note(recentQuery->value(8).toString().toStdString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!recentQuery->value(9).isNull()) {
|
|
||||||
info->set_assigned_mod_name(recentQuery->value(9).toString().toStdString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc.setResponseExtension(re);
|
rc.setResponseExtension(re);
|
||||||
|
|
@ -2167,7 +2118,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetServerStats(const Co
|
||||||
Response::ResponseCode AbstractServerSocketInterface::cmdReportStats(const Command_ReportStats & /*cmd */,
|
Response::ResponseCode AbstractServerSocketInterface::cmdReportStats(const Command_ReportStats & /*cmd */,
|
||||||
ResponseContainer &rc)
|
ResponseContainer &rc)
|
||||||
{
|
{
|
||||||
if (!sqlInterface->checkSql()) {
|
if (!databaseInterface->checkSql()) {
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2176,94 +2127,48 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportStats(const Comma
|
||||||
return Response::RespOk;
|
return Response::RespOk;
|
||||||
}
|
}
|
||||||
|
|
||||||
Response_ReportStats *re = new Response_ReportStats;
|
Server_DatabaseInterface::ReportStatsRecord stats;
|
||||||
|
if (!databaseInterface->getReportStats(stats)) {
|
||||||
QSqlQuery *overviewQuery = sqlInterface->prepareQuery(
|
|
||||||
"SELECT COUNT(*) AS total, "
|
|
||||||
"SUM(status IN ('open', 'assigned')) AS pending, "
|
|
||||||
"SUM(status = 'assigned') AS assigned, "
|
|
||||||
"SUM(status IN ('resolved', 'dismissed')) AS resolved, "
|
|
||||||
"SUM(created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)) AS last24h, "
|
|
||||||
"SUM(created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)) AS last7d, "
|
|
||||||
"SUM(created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)) AS last30d, "
|
|
||||||
"SUM(created_at >= DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)) AS this_week "
|
|
||||||
"FROM {prefix}_reports");
|
|
||||||
if (!sqlInterface->execSqlQuery(overviewQuery) || !overviewQuery->next()) {
|
|
||||||
delete re;
|
|
||||||
return Response::RespInternalError;
|
return Response::RespInternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
re->set_total_reports(overviewQuery->value(0).toInt());
|
auto *re = new Response_ReportStats;
|
||||||
re->set_total_pending(overviewQuery->value(1).toInt());
|
re->set_total_reports(stats.totalReports);
|
||||||
re->set_total_assigned(overviewQuery->value(2).toInt());
|
re->set_total_pending(stats.totalPending);
|
||||||
re->set_total_resolved(overviewQuery->value(3).toInt());
|
re->set_total_assigned(stats.totalAssigned);
|
||||||
re->set_reports_last_24h(overviewQuery->value(4).toInt());
|
re->set_total_resolved(stats.totalResolved);
|
||||||
re->set_reports_last_7d(overviewQuery->value(5).toInt());
|
re->set_reports_last_24h(stats.reportsLast24h);
|
||||||
re->set_reports_last_30d(overviewQuery->value(6).toInt());
|
re->set_reports_last_7d(stats.reportsLast7d);
|
||||||
re->set_reports_this_week(overviewQuery->value(7).toInt());
|
re->set_reports_last_30d(stats.reportsLast30d);
|
||||||
|
re->set_reports_this_week(stats.reportsThisWeek);
|
||||||
|
|
||||||
bool allQueriesOk = true;
|
if (stats.hasReportsLastWeek) {
|
||||||
|
re->set_reports_last_week(stats.reportsLastWeek);
|
||||||
QSqlQuery *lastWeekQuery =
|
|
||||||
sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_reports WHERE created_at >= DATE_SUB(CURDATE(), "
|
|
||||||
"INTERVAL WEEKDAY(CURDATE()) + 7 DAY) "
|
|
||||||
"AND created_at < DATE_SUB(CURDATE(), INTERVAL WEEKDAY(CURDATE()) DAY)");
|
|
||||||
if (sqlInterface->execSqlQuery(lastWeekQuery) && lastWeekQuery->next()) {
|
|
||||||
re->set_reports_last_week(lastWeekQuery->value(0).toInt());
|
|
||||||
} else {
|
|
||||||
allQueriesOk = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *avgResQuery = sqlInterface->prepareQuery(
|
if (stats.hasAvgResolutionHours) {
|
||||||
"SELECT AVG(TIMESTAMPDIFF(HOUR, created_at, resolution_time)) "
|
re->set_avg_resolution_hours(stats.avgResolutionHours);
|
||||||
"FROM {prefix}_reports WHERE status IN ('resolved','dismissed') AND resolution_time IS NOT NULL");
|
|
||||||
if (sqlInterface->execSqlQuery(avgResQuery) && avgResQuery->next()) {
|
|
||||||
if (!avgResQuery->value(0).isNull()) {
|
|
||||||
re->set_avg_resolution_hours(avgResQuery->value(0).toDouble());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allQueriesOk = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *catQuery = sqlInterface->prepareQuery(
|
for (const Server_DatabaseInterface::ReportCategoryCountRecord &count : stats.categoryCounts) {
|
||||||
"SELECT category, COUNT(*) AS cnt FROM {prefix}_reports GROUP BY category ORDER BY cnt DESC LIMIT 10");
|
ReportCategoryCount *cc = re->add_category_counts();
|
||||||
if (sqlInterface->execSqlQuery(catQuery)) {
|
cc->set_category(count.category.toStdString());
|
||||||
while (catQuery->next()) {
|
cc->set_count(count.count);
|
||||||
ReportCategoryCount *cc = re->add_category_counts();
|
|
||||||
cc->set_category(catQuery->value(0).toString().toStdString());
|
|
||||||
cc->set_count(catQuery->value(1).toInt());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allQueriesOk = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *topReportedQuery =
|
for (const Server_DatabaseInterface::ReportTopUserRecord &user : stats.topReportedUsers) {
|
||||||
sqlInterface->prepareQuery("SELECT reported_user_name, COUNT(*) AS cnt FROM {prefix}_reports "
|
ReportTopUser *tu = re->add_top_reported_users();
|
||||||
"GROUP BY reported_user_name ORDER BY cnt DESC LIMIT 10");
|
tu->set_user_name(user.userName.toStdString());
|
||||||
if (sqlInterface->execSqlQuery(topReportedQuery)) {
|
tu->set_count(user.count);
|
||||||
while (topReportedQuery->next()) {
|
|
||||||
ReportTopUser *tu = re->add_top_reported_users();
|
|
||||||
tu->set_user_name(topReportedQuery->value(0).toString().toStdString());
|
|
||||||
tu->set_count(topReportedQuery->value(1).toInt());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allQueriesOk = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSqlQuery *topReporterQuery =
|
for (const Server_DatabaseInterface::ReportTopUserRecord &user : stats.topReporters) {
|
||||||
sqlInterface->prepareQuery("SELECT reporter_name, COUNT(*) AS cnt FROM {prefix}_reports "
|
ReportTopUser *tu = re->add_top_reporters();
|
||||||
"GROUP BY reporter_name ORDER BY cnt DESC LIMIT 10");
|
tu->set_user_name(user.userName.toStdString());
|
||||||
if (sqlInterface->execSqlQuery(topReporterQuery)) {
|
tu->set_count(user.count);
|
||||||
while (topReporterQuery->next()) {
|
|
||||||
ReportTopUser *tu = re->add_top_reporters();
|
|
||||||
tu->set_user_name(topReporterQuery->value(0).toString().toStdString());
|
|
||||||
tu->set_count(topReporterQuery->value(1).toInt());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
allQueriesOk = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allQueriesOk) {
|
if (stats.allQueriesOk) {
|
||||||
servatrice->cacheReportStats(*re);
|
servatrice->cacheReportStats(*re);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue