PC-954 Allow staff to select what kind of report they'd like to handle

This commit is contained in:
Keir Nellyer 2016-10-12 17:15:48 +01:00
parent 15cf3a2bee
commit eb6d893414
8 changed files with 181 additions and 106 deletions

View File

@ -10,27 +10,27 @@ public enum ReportCategory
/** /**
* Global category, used for representing values which aren't tied to a specific category (such as abusive report statistics). * Global category, used for representing values which aren't tied to a specific category (such as abusive report statistics).
*/ */
GLOBAL(0), GLOBAL((short) 0),
/** /**
* Hacking category, for reports involving cheats of any sort. * Hacking category, for reports involving cheats of any sort.
*/ */
HACKING(1), HACKING((short) 1),
/** /**
* Chat Abuse category, for reports involving offensive comments made in chat. * Chat Abuse category, for reports involving offensive comments made in chat.
*/ */
CHAT_ABUSE(2), CHAT_ABUSE((short) 2),
/** /**
* Gameplay category, for reports specific to gameplay (such as bug exploits or issues with the map). * Gameplay category, for reports specific to gameplay (such as bug exploits or issues with the map).
*/ */
GAMEPLAY(3); GAMEPLAY((short) 3);
private final int _id; private final short _id;
private final String _name; private final String _name;
ReportCategory(int id) ReportCategory(short id)
{ {
_id = id; _id = id;
_name = WordUtils.capitalizeFully(name().replace('_', ' ')); _name = WordUtils.capitalizeFully(name().replace('_', ' '));
@ -41,7 +41,7 @@ public enum ReportCategory
* *
* @return the id * @return the id
*/ */
public int getId() public short getId()
{ {
return _id; return _id;
} }

View File

@ -6,13 +6,12 @@ import mineplex.core.account.CoreClient;
import mineplex.core.account.CoreClientManager; import mineplex.core.account.CoreClientManager;
import mineplex.core.command.CommandBase; import mineplex.core.command.CommandBase;
import mineplex.core.common.Rank; import mineplex.core.common.Rank;
import mineplex.core.common.util.BukkitFuture;
import mineplex.core.common.util.C; import mineplex.core.common.util.C;
import mineplex.core.common.util.F; import mineplex.core.common.util.F;
import mineplex.core.common.util.UtilPlayer; import mineplex.core.common.util.UtilPlayer;
import mineplex.core.report.ReportManager; import mineplex.core.report.ReportManager;
import mineplex.core.report.ReportPlugin; import mineplex.core.report.ReportPlugin;
import mineplex.core.report.ui.ReportCategoryPage; import mineplex.core.report.ui.ReportCreatePage;
/** /**
* The command used by players to create a report. * The command used by players to create a report.
@ -62,7 +61,7 @@ public class ReportCommand extends CommandBase<ReportPlugin>
else else
{ {
CoreClient suspectClient = clientManager.Get(suspect); CoreClient suspectClient = clientManager.Get(suspect);
new ReportCategoryPage(Plugin, reporter, reporterId, suspectClient, reason).openInventory(); new ReportCreatePage(Plugin, reporter, reporterId, suspectClient, reason).openInventory();
} }
} }
else else
@ -71,7 +70,7 @@ public class ReportCommand extends CommandBase<ReportPlugin>
{ {
if (suspectClient != null) if (suspectClient != null)
{ {
new ReportCategoryPage(Plugin, reporter, reporterId, suspectClient, reason).openInventory(); new ReportCreatePage(Plugin, reporter, reporterId, suspectClient, reason).openInventory();
} }
else else
{ {

View File

@ -1,24 +1,14 @@
package mineplex.core.report.command; package mineplex.core.report.command;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import mineplex.core.command.CommandBase; import mineplex.core.command.CommandBase;
import mineplex.core.common.Rank; import mineplex.core.common.Rank;
import mineplex.core.common.util.BukkitFuture;
import mineplex.core.common.util.C; import mineplex.core.common.util.C;
import mineplex.core.common.util.F; import mineplex.core.common.util.F;
import mineplex.core.common.util.UtilPlayer; import mineplex.core.common.util.UtilPlayer;
import mineplex.core.report.ReportManager;
import mineplex.core.report.ReportPlugin; import mineplex.core.report.ReportPlugin;
import mineplex.core.report.data.Report; import mineplex.core.report.ui.ReportHandlePage;
import mineplex.core.report.data.ReportRepository;
/** /**
* When executed, the user is appointed handler of the most important report in the report queue (if any). * When executed, the user is appointed handler of the most important report in the report queue (if any).
@ -36,76 +26,9 @@ public class ReportHandleCommand extends CommandBase<ReportPlugin>
{ {
if (args == null || args.length == 0) if (args == null || args.length == 0)
{ {
ReportManager reportManager = Plugin.getReportManager();
ReportRepository reportRepository = reportManager.getReportRepository();
int accountId = _commandCenter.GetClientManager().getAccountId(player); int accountId = _commandCenter.GetClientManager().getAccountId(player);
ReportHandlePage reportHandlePage = new ReportHandlePage(Plugin, player, accountId);
reportManager.isHandlingReport(player).whenComplete((handlingReport, throwable) -> reportHandlePage.openInventory();
{
if (throwable == null)
{
if (!handlingReport)
{
Map<Report, Double> reportPriorities = Collections.synchronizedMap(new HashMap<>());
boolean devMode = reportManager.isDevMode(player.getUniqueId());
// the below fetches the ids of all unhandled reports and gets a Report object for each of these ids
// the priority of the report is then calculated and the results placed in a map
reportRepository.getUnhandledReports(accountId, devMode).thenCompose(reportRepository::getReports).thenAccept(reports ->
CompletableFuture.allOf(reports.stream().map(report ->
reportManager.calculatePriority(report).thenAccept(priority ->
{
if (priority > 0)
{
reportPriorities.put(report, priority);
}
else
{
// mark the report as expired to keep the database clean
// and reduce future query time
reportManager.expireReport(report);
}
}
)
).toArray(CompletableFuture[]::new)).join()
).thenApply(aVoid ->
{
Map.Entry<Report, Double> mostImportant = null;
for (Map.Entry<Report, Double> entry : reportPriorities.entrySet())
{
if (mostImportant == null || (double) entry.getValue() > mostImportant.getValue())
{
mostImportant = entry;
}
}
return mostImportant == null ? null : mostImportant.getKey();
}).thenCompose(BukkitFuture.accept(report ->
{
if (report != null)
{
reportManager.handleReport(report, player);
}
else
{
UtilPlayer.message(player, F.main(Plugin.getName(), C.cRed + "No report found, report queue is empty."));
}
}));
}
else
{
Bukkit.getScheduler().runTask(Plugin.getPlugin(), () ->
UtilPlayer.message(player, F.main(Plugin.getName(), C.cRed + "You are already handling a report.")));
}
}
else
{
UtilPlayer.message(player, F.main(Plugin.getName(), C.cRed + "An error occurred, please try again later."));
Plugin.getPlugin().getLogger().log(Level.SEVERE, "Error whilst checking for reports being handled by " + player.getName(), throwable);
}
});
} }
else else
{ {

View File

@ -77,7 +77,8 @@ public class ReportRepository
" LEFT JOIN reportResults ON reports.id = reportResults.reportId\n" + " LEFT JOIN reportResults ON reports.id = reportResults.reportId\n" +
" LEFT JOIN reportHandlers ON reports.id = reportHandlers.reportId\n" + " LEFT JOIN reportHandlers ON reports.id = reportHandlers.reportId\n" +
" LEFT JOIN reportReasons ON reports.id = reportReasons.reportId\n" + " LEFT JOIN reportReasons ON reports.id = reportReasons.reportId\n" +
"WHERE reportResults.reportId IS NULL\n" + "WHERE reports.categoryId = ?\n" +
" AND reportResults.reportId IS NULL\n" +
" /* Bypass for testing purposes or check player isn't suspect */\n" + " /* Bypass for testing purposes or check player isn't suspect */\n" +
" AND (? IS TRUE OR reports.suspectId != ?)\n" + " AND (? IS TRUE OR reports.suspectId != ?)\n" +
" /* If team is assigned, make sure user is member of team */\n" + " /* If team is assigned, make sure user is member of team */\n" +
@ -157,7 +158,7 @@ public class ReportRepository
* @param devMode if true, allows various restrictions to be bypassed * @param devMode if true, allows various restrictions to be bypassed
* @return the ids of unhandled reports the supplied account is allowed to handle * @return the ids of unhandled reports the supplied account is allowed to handle
*/ */
public CompletableFuture<List<Long>> getUnhandledReports(int accountId, boolean devMode) public CompletableFuture<List<Long>> getUnhandledReports(int accountId, ReportCategory category, boolean devMode)
{ {
CompletableFuture<List<Long>> future = CompletableFuture.supplyAsync(() -> CompletableFuture<List<Long>> future = CompletableFuture.supplyAsync(() ->
{ {
@ -166,12 +167,13 @@ public class ReportRepository
try (Connection connection = DBPool.getAccount().getConnection()) try (Connection connection = DBPool.getAccount().getConnection())
{ {
PreparedStatement preparedStatement = connection.prepareStatement(GET_UNHANDLED_REPORTS); PreparedStatement preparedStatement = connection.prepareStatement(GET_UNHANDLED_REPORTS);
preparedStatement.setBoolean(1, devMode); preparedStatement.setShort(1, category.getId());
preparedStatement.setInt(2, accountId); preparedStatement.setBoolean(2, devMode);
preparedStatement.setInt(3, accountId); preparedStatement.setInt(3, accountId);
preparedStatement.setInt(4, accountId); preparedStatement.setInt(4, accountId);
preparedStatement.setBoolean(5, devMode); preparedStatement.setInt(5, accountId);
preparedStatement.setInt(6, accountId); preparedStatement.setBoolean(6, devMode);
preparedStatement.setInt(7, accountId);
ResultSet resultSet = preparedStatement.executeQuery(); ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) while (resultSet.next())

View File

@ -13,7 +13,7 @@ import mineplex.core.itemstack.ItemBuilder;
import mineplex.core.report.ReportCategory; import mineplex.core.report.ReportCategory;
/** /**
* Represents a clickable button in a {@link ReportCategoryPage} which determines the type of infraction a player has committed. * Represents a clickable button in a {@link ReportCreatePage} which determines the type of infraction a player has committed.
*/ */
public class ReportCategoryButton extends SimpleGuiItem public class ReportCategoryButton extends SimpleGuiItem
{ {
@ -39,24 +39,29 @@ public class ReportCategoryButton extends SimpleGuiItem
put(ReportCategory.GAMEPLAY, itemGameplay); put(ReportCategory.GAMEPLAY, itemGameplay);
}}; }};
private final ReportCategoryPage _reportCategoryPage; private final ReportCategoryCallback _callback;
private final ReportCategory _category; private final ReportCategory _category;
public ReportCategoryButton(ReportCategoryPage reportCategoryPage, ReportCategory reportCategory) public ReportCategoryButton(ReportCategoryCallback callback, ReportCategory reportCategory)
{ {
this(reportCategoryPage, reportCategory, ITEM_STACKS.get(reportCategory)); this(callback, reportCategory, ITEM_STACKS.get(reportCategory));
} }
public ReportCategoryButton(ReportCategoryPage reportCategoryPage, ReportCategory reportCategory, ItemStack itemStack) public ReportCategoryButton(ReportCategoryCallback callback, ReportCategory reportCategory, ItemStack itemStack)
{ {
super(itemStack); super(itemStack);
_reportCategoryPage = reportCategoryPage; _callback = callback;
_category = reportCategory; _category = reportCategory;
} }
public ReportCategory getCategory()
{
return _category;
}
@Override @Override
public void click(ClickType clickType) public void click(ClickType clickType)
{ {
_reportCategoryPage.addReport(_category); _callback.click(this);
} }
} }

View File

@ -0,0 +1,13 @@
package mineplex.core.report.ui;
/**
* Allows re-use of the {@link ReportCategoryButton} class.
*/
public interface ReportCategoryCallback
{
/**
* Invoked when a category button is clicked.
* @param button The button that was clicked
*/
void click(ReportCategoryButton button);
}

View File

@ -21,7 +21,7 @@ import mineplex.core.report.ReportPlugin;
/** /**
* User interface shown to a player when reporting another player. * User interface shown to a player when reporting another player.
*/ */
public class ReportCategoryPage extends SimpleGui public class ReportCreatePage extends SimpleGui implements ReportCategoryCallback
{ {
private final ReportPlugin _plugin; private final ReportPlugin _plugin;
private final Player _reporter; private final Player _reporter;
@ -29,7 +29,7 @@ public class ReportCategoryPage extends SimpleGui
private final CoreClient _suspect; private final CoreClient _suspect;
private final String _reason; private final String _reason;
public ReportCategoryPage(ReportPlugin plugin, Player reporter, int reporterId, CoreClient suspect, String reason) public ReportCreatePage(ReportPlugin plugin, Player reporter, int reporterId, CoreClient suspect, String reason)
{ {
super(plugin.getPlugin(), reporter, "Report " + suspect.getName(), 9 * 3); super(plugin.getPlugin(), reporter, "Report " + suspect.getName(), 9 * 3);
@ -111,4 +111,9 @@ public class ReportCategoryPage extends SimpleGui
HandlerList.unregisterAll(this); HandlerList.unregisterAll(this);
} }
@Override
public void click(ReportCategoryButton button)
{
addReport(button.getCategory());
}
} }

View File

@ -0,0 +1,128 @@
package mineplex.core.report.ui;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import mineplex.core.common.util.BukkitFuture;
import mineplex.core.common.util.C;
import mineplex.core.common.util.F;
import mineplex.core.common.util.UtilPlayer;
import mineplex.core.gui.SimpleGui;
import mineplex.core.report.ReportCategory;
import mineplex.core.report.ReportManager;
import mineplex.core.report.ReportPlugin;
import mineplex.core.report.data.Report;
import mineplex.core.report.data.ReportRepository;
/**
* An interface which allows the user to select the type of report they'd like to handle.
*/
public class ReportHandlePage extends SimpleGui implements ReportCategoryCallback
{
private final ReportPlugin _plugin;
private final Player _handler;
private final int _handlerId;
public ReportHandlePage(ReportPlugin plugin, Player handler, int handlerId)
{
super(plugin.getPlugin(), handler, "Report Type Selection", 9);
_plugin = plugin;
_handler = handler;
_handlerId = handlerId;
buildPage();
}
private void buildPage()
{
setItem(11, new ReportCategoryButton(this, ReportCategory.HACKING));
setItem(13, new ReportCategoryButton(this, ReportCategory.CHAT_ABUSE));
setItem(15, new ReportCategoryButton(this, ReportCategory.GAMEPLAY));
}
@Override
public void click(ReportCategoryButton button)
{
handleReport(button.getCategory());
}
public void handleReport(ReportCategory category)
{
ReportManager reportManager = _plugin.getReportManager();
ReportRepository reportRepository = reportManager.getReportRepository();
reportManager.isHandlingReport(_handler).whenComplete((handlingReport, throwable) ->
{
if (throwable == null)
{
if (!handlingReport)
{
Map<Report, Double> reportPriorities = Collections.synchronizedMap(new HashMap<>());
boolean devMode = reportManager.isDevMode(_handler.getUniqueId());
// the below fetches the ids of all unhandled reports and gets a Report object for each of these ids
// the priority of the report is then calculated and the results placed in a map
reportRepository.getUnhandledReports(_handlerId, category, devMode).thenCompose(reportRepository::getReports).thenAccept(reports ->
CompletableFuture.allOf(reports.stream().map(report ->
reportManager.calculatePriority(report).thenAccept(priority ->
{
if (priority > 0)
{
reportPriorities.put(report, priority);
}
else
{
// mark the report as expired to keep the database clean
// and reduce future query time
reportManager.expireReport(report);
}
}
)
).toArray(CompletableFuture[]::new)).join()
).thenApply(aVoid ->
{
Map.Entry<Report, Double> mostImportant = null;
for (Map.Entry<Report, Double> entry : reportPriorities.entrySet())
{
if (mostImportant == null || (double) entry.getValue() > mostImportant.getValue())
{
mostImportant = entry;
}
}
return mostImportant == null ? null : mostImportant.getKey();
}).thenCompose(BukkitFuture.accept(report ->
{
if (report != null)
{
reportManager.handleReport(report, _handler);
}
else
{
UtilPlayer.message(_handler, F.main(_plugin.getName(), C.cRed + "No report found, report queue is empty."));
}
}));
}
else
{
Bukkit.getScheduler().runTask(_plugin.getPlugin(), () ->
UtilPlayer.message(_handler, F.main(_plugin.getName(), C.cRed + "You are already handling a report.")));
}
}
else
{
UtilPlayer.message(_handler, F.main(_plugin.getName(), C.cRed + "An error occurred, please try again later."));
_plugin.getPlugin().getLogger().log(Level.SEVERE, "Error whilst checking for reports being handled by " + _handler.getName(), throwable);
}
});
}
}