Store graph metadata

TODO
- Tests for the storage code
- Storing multiple points
- Starting player graph samplers
- Settings for samplers
- The fetch side for the graphs.
This commit is contained in:
Aurora Lahtela 2024-10-05 12:25:13 +03:00
parent 46f61e7403
commit 58456a0623
27 changed files with 1673 additions and 111 deletions

View File

@ -68,24 +68,6 @@ public @interface GraphPointProvider {
*/
XAxisType xAxisType() default XAxisType.DATE_MILLIS;
/**
* Should the x-axis of the graph be visualized so that it starts and stops at specific value, growing if necessary.
*
* @return false by default
* @see GraphPointProvider#xAxisSoftMin()
* @see GraphPointProvider#xAxisSoftMax()
*/
boolean xAxisSoftLimits() default false;
/**
* Should the y-axis of the graph be visualized so that it starts and stops at specific value, growing if necessary.
*
* @return false by default
* @see GraphPointProvider#yAxisSoftMin()
* @see GraphPointProvider#yAxisSoftMax()
*/
boolean yAxisSoftLimits() default false;
/**
* Minimum for x-axis, growing if necessary.
*
@ -137,7 +119,7 @@ public @interface GraphPointProvider {
/**
* Hex color string of each series.
* <p>
* Allows you to control color of your data.
* Allows you to control color of your data. You need to return hex codes at most 7 chars in length.
* <p>
* If this is left unspecified, a predefined color series used in visualization will be used.
*
@ -178,6 +160,8 @@ public @interface GraphPointProvider {
* <p>
* Automatic aggregate numbers (As if using {@link NumberProvider}) will be added to the same {@link Tab} if these are defined.
*
* If the graph has multiple series (multiple y values) there will be no aggregates.
*
* @return None by default.
*/
Aggregates[] supportedAggregateFunctions() default {};

View File

@ -105,82 +105,11 @@ public final class ExtensionExtractor {
@Deprecated
public void extractAnnotationInformation() {/* no-op */}
private void extractMethods() {
methods = new EnumMap<>(ExtensionMethod.ParameterType.class);
methods.put(ExtensionMethod.ParameterType.SERVER_NONE, new ExtensionMethods());
methods.put(ExtensionMethod.ParameterType.PLAYER_STRING, new ExtensionMethods());
methods.put(ExtensionMethod.ParameterType.PLAYER_UUID, new ExtensionMethods());
methods.put(ExtensionMethod.ParameterType.GROUP, new ExtensionMethods());
conditionalMethods = new ArrayList<>();
tabAnnotations = new ArrayList<>();
for (ExtensionMethod method : getExtensionMethods()) {
if (method.isInaccessible()) {
continue;
}
try {
method.makeAccessible();
} catch (SecurityException failedToMakeAccessible) {
throw new IllegalArgumentException(extensionName + "." + method.getMethodName() + " could not be made accessible: " +
failedToMakeAccessible.getMessage(), failedToMakeAccessible);
}
method.getAnnotation(BooleanProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addBooleanMethod(method);
});
method.getAnnotation(NumberProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addNumberMethod(method);
});
method.getAnnotation(DoubleProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addDoubleMethod(method);
});
method.getAnnotation(PercentageProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addPercentageMethod(method);
});
method.getAnnotation(StringProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addStringMethod(method);
});
method.getAnnotation(ComponentProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addComponentMethod(method);
});
method.getAnnotation(TableProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addTableMethod(method);
});
method.getAnnotation(GroupProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addGroupMethod(method);
});
method.getAnnotation(DataBuilderProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addDataBuilderMethod(method);
});
method.getAnnotation(GraphPointProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addGraphPointProviderMethod(method);
});
method.getAnnotation(GraphHistoryPointsProvider.class).ifPresent(annotation -> {
validateMethod(method);
methods.get(method.getParameterType()).addGraphHistoryPointsProviderMethod(method);
});
method.getAnnotation(Conditional.class).ifPresent(annotation -> conditionalMethods.add(method.getMethod()));
method.getAnnotation(Tab.class).ifPresent(tabAnnotations::add);
}
if (methods.values().stream().allMatch(ExtensionMethods::isEmpty)) {
throw new IllegalArgumentException(extensionName + " class had no methods annotated with a Provider annotation");
}
validateGraphPointProviderMethodsExistForHistoryProviders();
validateConditionals();
public static boolean actuallySupportsStacking(GraphPointProvider annotation) {
boolean supportsStacking = annotation.supportsStacking();
boolean moreUnits = new HashSet<>(Arrays.asList(annotation.unitNames())).size() > 1;
boolean moreFormats = new HashSet<>(Arrays.asList(annotation.valueFormats())).size() > 1;
return supportsStacking && !moreFormats && !moreUnits;
}
private void validateGraphPointProviderMethodsExistForHistoryProviders() {
@ -382,6 +311,86 @@ public final class ExtensionExtractor {
}
}
private void extractMethods() {
methods = new EnumMap<>(ExtensionMethod.ParameterType.class);
methods.put(ExtensionMethod.ParameterType.SERVER_NONE, new ExtensionMethods());
methods.put(ExtensionMethod.ParameterType.PLAYER_STRING, new ExtensionMethods());
methods.put(ExtensionMethod.ParameterType.PLAYER_UUID, new ExtensionMethods());
methods.put(ExtensionMethod.ParameterType.GROUP, new ExtensionMethods());
conditionalMethods = new ArrayList<>();
tabAnnotations = new ArrayList<>();
for (ExtensionMethod method : getExtensionMethods()) {
if (method.isInaccessible()) {
continue;
}
try {
method.makeAccessible();
} catch (SecurityException failedToMakeAccessible) {
throw new IllegalArgumentException(extensionName + "." + method.getMethodName() + " could not be made accessible: " +
failedToMakeAccessible.getMessage(), failedToMakeAccessible);
}
validateMethodAnnotationPropertyLength(method.getMethod().getName(), "methodName", 50, method.getMethod());
method.getAnnotation(BooleanProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addBooleanMethod(method);
});
method.getAnnotation(NumberProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addNumberMethod(method);
});
method.getAnnotation(DoubleProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addDoubleMethod(method);
});
method.getAnnotation(PercentageProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addPercentageMethod(method);
});
method.getAnnotation(StringProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addStringMethod(method);
});
method.getAnnotation(ComponentProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addComponentMethod(method);
});
method.getAnnotation(TableProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addTableMethod(method);
});
method.getAnnotation(GroupProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addGroupMethod(method);
});
method.getAnnotation(DataBuilderProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addDataBuilderMethod(method);
});
method.getAnnotation(GraphPointProvider.class).ifPresent(annotation -> {
validateMethod(method, annotation);
methods.get(method.getParameterType()).addGraphPointProviderMethod(method);
});
method.getAnnotation(GraphHistoryPointsProvider.class).ifPresent(annotation -> {
validateMethod(method);
methods.get(method.getParameterType()).addGraphHistoryPointsProviderMethod(method);
});
method.getAnnotation(Conditional.class).ifPresent(annotation -> conditionalMethods.add(method.getMethod()));
method.getAnnotation(Tab.class).ifPresent(tabAnnotations::add);
}
if (methods.values().stream().allMatch(ExtensionMethods::isEmpty)) {
throw new IllegalArgumentException(extensionName + " class had no methods annotated with a Provider annotation");
}
validateGraphPointProviderMethodsExistForHistoryProviders();
validateConditionals();
}
private void validateConditionals() {
// Make sure that all methods annotated with Conditional have a Provider annotation
for (Method conditionalMethod : conditionalMethods) {

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.extension.extractor;
import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.Group;
import org.apache.commons.lang3.StringUtils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@ -74,7 +75,7 @@ public class ExtensionMethod {
}
public String getMethodName() {
return getMethod().getName();
return StringUtils.truncate(getMethod().getName(), 50);
}
/**

View File

@ -25,13 +25,37 @@ package com.djrapitops.plan.extension.graph;
*/
public enum Aggregates {
/**
* SUM that can be filtered by time value x, eg. last 30 days
*/
SUM_OVER_TIME,
/**
* MEAN (average) that can be filtered by time value x, eg. last 30 days
*/
MEAN_OVER_TIME,
/**
* MIN that can be filtered by time value x, eg. last 30 days
*/
MIN_OVER_TIME,
/**
* MAX that can be filtered by time value x, eg. last 30 days
*/
MAX_OVER_TIME,
/**
* Cumulative SUM over all points
*/
SUM_TOTAL,
/**
* MEAN (average) over all points
*/
MEAN_TOTAL,
/**
* Minimum seen point.
*/
MIN_TOTAL,
/**
* Maximum seen point.
*/
MAX_TOTAL,
}

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.extension.implementation.ExtensionRegister;
import com.djrapitops.plan.extension.implementation.ExtensionWrapper;
import com.djrapitops.plan.extension.implementation.builder.ExtDataBuilder;
import com.djrapitops.plan.extension.implementation.providers.gathering.DataValueGatherer;
import com.djrapitops.plan.extension.implementation.providers.gathering.GraphSamplers;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.identification.UUIDUtility;
import com.djrapitops.plan.processing.Processing;
@ -56,6 +57,7 @@ public class ExtensionSvc implements ExtensionService {
private final ServerInfo serverInfo;
private final Processing processing;
private final ExtensionRegister extensionRegister;
private final GraphSamplers graphSamplers;
private final UUIDUtility uuidUtility;
private final PluginLogger logger;
private final ErrorLogger errorLogger;
@ -71,6 +73,7 @@ public class ExtensionSvc implements ExtensionService {
ServerInfo serverInfo,
Processing processing,
ExtensionRegister extensionRegister,
GraphSamplers graphSamplers,
UUIDUtility uuidUtility,
PluginLogger logger,
ErrorLogger errorLogger
@ -81,6 +84,7 @@ public class ExtensionSvc implements ExtensionService {
this.serverInfo = serverInfo;
this.processing = processing;
this.extensionRegister = extensionRegister;
this.graphSamplers = graphSamplers;
this.uuidUtility = uuidUtility;
this.logger = logger;
this.errorLogger = errorLogger;
@ -123,6 +127,8 @@ public class ExtensionSvc implements ExtensionService {
DataValueGatherer gatherer = new DataValueGatherer(extension, dbSystem, componentService, serverInfo, errorLogger);
gatherer.storeExtensionInformation();
extensionGatherers.put(pluginName, gatherer);
graphSamplers.registerGraphSamplers(extension);
graphSamplers.storePlayerGraphMetadata(extension);
processing.submitNonCritical(() -> updateServerValues(gatherer, CallEvents.SERVER_EXTENSION_REGISTER));

View File

@ -21,6 +21,7 @@ import com.djrapitops.plan.extension.DataExtension;
import com.djrapitops.plan.extension.FormatType;
import com.djrapitops.plan.extension.annotation.BooleanProvider;
import com.djrapitops.plan.extension.annotation.Conditional;
import com.djrapitops.plan.extension.annotation.GraphPointProvider;
import com.djrapitops.plan.extension.annotation.PluginInfo;
import com.djrapitops.plan.extension.builder.DataValue;
import com.djrapitops.plan.extension.builder.ValueBuilder;
@ -135,6 +136,10 @@ public class ExtValueBuilder implements ValueBuilder {
return getProviderInformation(true, false, null);
}
public ProviderInformation buildProviderInfo(@SuppressWarnings("unused") GraphPointProvider annotation) {
return getProviderInformation();
}
private ProviderInformation getProviderInformation(boolean percentage, boolean component, String providedCondition) {
ProviderInformation.Builder builder = ProviderInformation.builder(pluginName)
.setName(providerName != null ? providerName

View File

@ -0,0 +1,100 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
import com.djrapitops.plan.extension.annotation.GraphPointProvider;
import com.djrapitops.plan.extension.annotation.Tab;
import com.djrapitops.plan.extension.builder.ValueBuilder;
import com.djrapitops.plan.extension.extractor.ExtensionMethod;
import com.djrapitops.plan.extension.extractor.ExtensionMethods;
import com.djrapitops.plan.extension.icon.Icon;
import com.djrapitops.plan.extension.implementation.ExtensionWrapper;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.extension.implementation.builder.ExtValueBuilder;
import com.djrapitops.plan.extension.implementation.providers.Parameters;
import com.djrapitops.plan.extension.implementation.providers.ProviderIdentifier;
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.StoreGraphPointProviderTransaction;
import com.djrapitops.plan.identification.ServerInfo;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.Map;
/**
* This is a utility class for managing extension graph sampling.
*
* @author AuroraLS3
*/
@Singleton
public class GraphSamplers {
private final ServerInfo serverInfo;
private final DBSystem dbSystem;
private final RunnableFactory runnableFactory;
private final ErrorLogger errorLogger;
@Inject
public GraphSamplers(ServerInfo serverInfo, DBSystem dbSystem, RunnableFactory runnableFactory, ErrorLogger errorLogger) {
this.serverInfo = serverInfo;
this.dbSystem = dbSystem;
this.runnableFactory = runnableFactory;
this.errorLogger = errorLogger;
}
public void registerGraphSamplers(ExtensionWrapper extension) {
Map<ExtensionMethod.ParameterType, ExtensionMethods> methods = extension.getMethods();
ExtensionMethods extensionMethods = methods.get(ExtensionMethod.ParameterType.SERVER_NONE);
Parameters parameters = Parameters.server(serverInfo.getServerUUID());
List<ExtensionMethod> graphPointProviders = extensionMethods.getGraphPointProviders();
for (ExtensionMethod graphPointProvider : graphPointProviders) {
storeGraphMetadata(extension, graphPointProvider);
ServerGraphSampler sampler = new ServerGraphSampler(extension, graphPointProvider, dbSystem, parameters,
new ProviderIdentifier(serverInfo.getServerUUID(), extension.getPluginName(), graphPointProvider.getMethodName()),
errorLogger
);
sampler.register(runnableFactory);
}
}
private void storeGraphMetadata(ExtensionWrapper extension, ExtensionMethod provider) {
GraphPointProvider annotation = provider.getExistingAnnotation(GraphPointProvider.class);
ValueBuilder valueBuilder = extension.getExtension().valueBuilder(annotation.displayName())
.showOnTab(provider.getAnnotationOrNull(Tab.class))
.methodName(provider)
.priority(annotation.priority())
.icon(Icon.called("question").build());
ProviderInformation info = ((ExtValueBuilder) valueBuilder).buildProviderInfo(annotation);
dbSystem.getDatabase().executeTransaction(new StoreGraphPointProviderTransaction(annotation, provider, info, serverInfo.getServerUUID()));
}
public void storePlayerGraphMetadata(ExtensionWrapper extension) {
Map<ExtensionMethod.ParameterType, ExtensionMethods> methods = extension.getMethods();
for (ExtensionMethod graphPointProvider : methods.get(ExtensionMethod.ParameterType.PLAYER_UUID).getGraphPointProviders()) {
storeGraphMetadata(extension, graphPointProvider);
}
for (ExtensionMethod graphPointProvider : methods.get(ExtensionMethod.ParameterType.PLAYER_STRING).getGraphPointProviders()) {
storeGraphMetadata(extension, graphPointProvider);
}
}
}

View File

@ -0,0 +1,87 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.extension.annotation.GraphPointProvider;
import com.djrapitops.plan.extension.extractor.ExtensionMethod;
import com.djrapitops.plan.extension.graph.DataPoint;
import com.djrapitops.plan.extension.implementation.ExtensionWrapper;
import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
import com.djrapitops.plan.extension.implementation.providers.Parameters;
import com.djrapitops.plan.extension.implementation.providers.ProviderIdentifier;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StorePlayerGraphPoint;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
/**
* @author AuroraLS3
*/
public class PlayerGraphSampler extends TaskSystem.Task {
private final ExtensionWrapper extension;
private final DBSystem dbSystem;
private final ExtensionMethod provider;
private final GraphPointProvider annotation;
private final Parameters.PlayerParameters parameters;
private final ProviderIdentifier providerIdentifier;
private final ErrorLogger errorLogger;
public PlayerGraphSampler(ExtensionWrapper extension, ExtensionMethod provider, DBSystem dbSystem, Parameters.PlayerParameters parameters, ProviderIdentifier providerIdentifier, ErrorLogger errorLogger) {
this.extension = extension;
this.provider = provider;
annotation = provider.getExistingAnnotation(GraphPointProvider.class);
this.dbSystem = dbSystem;
this.parameters = parameters;
this.providerIdentifier = providerIdentifier;
this.errorLogger = errorLogger;
}
private DataPoint callMethod() {
return new MethodWrapper<>(provider.getMethod(), DataPoint.class)
.callMethod(extension.getExtension(), parameters);
}
@Override
public void register(RunnableFactory runnableFactory) {
runnableFactory.create(this)
.runTaskTimerAsynchronously(0, annotation.sampleInterval(), annotation.sampleIntervalUnit());
}
public void unregister() {
cancel();
}
@Override
public void run() {
try {
DataPoint dataPoint = callMethod();
if (dataPoint == null) return;
dbSystem.getDatabase().executeTransaction(new StorePlayerGraphPoint(dataPoint, parameters.getPlayerUUID(), providerIdentifier));
} catch (DataExtensionMethodCallException e) {
errorLogger.warn(e, ErrorContext.builder()
.related(providerIdentifier)
.whatToDo("Player Graph sampler for " + providerIdentifier.getPluginName() + "." + providerIdentifier.getProviderName() + " ran into error and was disabled. You can disable the plugin from Plan config and report this.")
.build());
// TODO #2544 Prevent further registrations of the player graph sampler.
cancel();
}
}
}

View File

@ -0,0 +1,88 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.providers.gathering;
import com.djrapitops.plan.TaskSystem;
import com.djrapitops.plan.exceptions.DataExtensionMethodCallException;
import com.djrapitops.plan.extension.annotation.GraphPointProvider;
import com.djrapitops.plan.extension.extractor.ExtensionMethod;
import com.djrapitops.plan.extension.graph.DataPoint;
import com.djrapitops.plan.extension.implementation.ExtensionWrapper;
import com.djrapitops.plan.extension.implementation.providers.MethodWrapper;
import com.djrapitops.plan.extension.implementation.providers.Parameters;
import com.djrapitops.plan.extension.implementation.providers.ProviderIdentifier;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.StoreServerGraphPoint;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.utilities.logging.ErrorContext;
import com.djrapitops.plan.utilities.logging.ErrorLogger;
import net.playeranalytics.plugin.scheduling.RunnableFactory;
/**
* @author AuroraLS3
*/
public class ServerGraphSampler extends TaskSystem.Task {
private final ExtensionWrapper extension;
private final DBSystem dbSystem;
private final ExtensionMethod provider;
private final GraphPointProvider annotation;
private final Parameters parameters;
private final ProviderIdentifier providerIdentifier;
private final ErrorLogger errorLogger;
public ServerGraphSampler(
ExtensionWrapper extension,
ExtensionMethod provider,
DBSystem dbSystem,
Parameters parameters,
ProviderIdentifier providerIdentifier,
ErrorLogger errorLogger
) {
this.extension = extension;
this.provider = provider;
annotation = provider.getExistingAnnotation(GraphPointProvider.class);
this.dbSystem = dbSystem;
this.parameters = parameters;
this.providerIdentifier = providerIdentifier;
this.errorLogger = errorLogger;
}
private DataPoint callMethod() {
return new MethodWrapper<>(provider.getMethod(), DataPoint.class)
.callMethod(extension.getExtension(), parameters);
}
@Override
public void register(RunnableFactory runnableFactory) {
runnableFactory.create(this)
.runTaskTimerAsynchronously(0, annotation.sampleInterval(), annotation.sampleIntervalUnit());
}
@Override
public void run() {
try {
DataPoint dataPoint = callMethod();
if (dataPoint == null) return;
dbSystem.getDatabase().executeTransaction(new StoreServerGraphPoint(dataPoint, providerIdentifier));
} catch (DataExtensionMethodCallException e) {
errorLogger.warn(e, ErrorContext.builder()
.related(providerIdentifier)
.whatToDo("Graph sampler for " + providerIdentifier.getPluginName() + "." + providerIdentifier.getProviderName() + " ran into error and was disabled. You can disable the plugin from Plan config and report this.")
.build());
cancel();
}
}
}

View File

@ -0,0 +1,59 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.queries;
import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.ExtensionGraphMetadataTable;
import org.intellij.lang.annotations.Language;
import java.util.Collection;
import java.util.List;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* @author AuroraLS3
*/
public class ExtensionGraphQueries {
private ExtensionGraphQueries() {
/* Static method class */
}
public static Query<List<String>> findGraphTableNames() {
@Language("SQL")
String sql = SELECT + ExtensionGraphMetadataTable.GRAPH_TABLE_NAME + FROM + ExtensionGraphMetadataTable.TABLE_NAME;
return db -> db.queryList(sql, row -> row.getString(1));
}
public static Query<List<String>> findGraphTableNames(ExtensionGraphMetadataTable.TableType type) {
@Language("SQL")
String sql = SELECT + ExtensionGraphMetadataTable.GRAPH_TABLE_NAME +
FROM + ExtensionGraphMetadataTable.TABLE_NAME +
WHERE + ExtensionGraphMetadataTable.TABLE_TYPE + "=?";
return db -> db.queryList(sql, row -> row.getString(1), type.getType());
}
public static Query<List<String>> findGraphTableNames(Collection<Integer> providerIds) {
@Language("SQL")
String sql = SELECT + ExtensionGraphMetadataTable.GRAPH_TABLE_NAME +
FROM + ExtensionGraphMetadataTable.TABLE_NAME +
WHERE + ExtensionGraphMetadataTable.PROVIDER_ID + " IN (" + Sql.nParameters(providerIds.size()) + ")";
return db -> db.queryList(sql, row -> row.getString(1), providerIds);
}
}

View File

@ -0,0 +1,65 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.providers;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.extension.implementation.providers.ProviderIdentifier;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionProviderTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.ExtensionGraphMetadataTable;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.Transaction;
import org.jetbrains.annotations.NotNull;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author AuroraLS3
*/
public class ExpandGraphColumnCountTransaction extends Transaction {
private final ProviderIdentifier identifier;
private final int colCount;
public ExpandGraphColumnCountTransaction(ProviderIdentifier identifier, int colCount) {
this.identifier = identifier;
this.colCount = colCount;
}
@Override
protected void performOperations() {
Integer existingColCount = query(db -> db.queryOptional(ExtensionGraphMetadataTable.STATEMENT_SELECT_COLUMN_COUNT,
row -> row.getInt(ExtensionGraphMetadataTable.COLUMN_COUNT),
identifier.getProviderName(), identifier.getPluginName(), identifier.getServerUUID()))
.orElseThrow(() -> new DBOpException("Graph table metadata does not exist"));
if (existingColCount < colCount) {
ExtensionGraphMetadataTable.addColumnsStatements(identifier.getPluginName(), identifier.getProviderName(), existingColCount, colCount)
.forEach(this::execute);
execute(updateColumnCount());
}
}
private @NotNull ExecStatement updateColumnCount() {
return new ExecStatement(ExtensionGraphMetadataTable.UPDATE_COLUMN_COUNT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, colCount);
ExtensionProviderTable.set3PluginValuesToStatement(statement, 2, identifier.getProviderName(), identifier.getPluginName(), identifier.getServerUUID());
}
};
}
}

View File

@ -0,0 +1,307 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.providers;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.extension.FormatType;
import com.djrapitops.plan.extension.annotation.GraphPointProvider;
import com.djrapitops.plan.extension.extractor.ExtensionExtractor;
import com.djrapitops.plan.extension.extractor.ExtensionMethod;
import com.djrapitops.plan.extension.graph.Aggregates;
import com.djrapitops.plan.extension.implementation.ProviderInformation;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionProviderTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionTabTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.*;
import com.djrapitops.plan.storage.database.transactions.ExecBatchStatement;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.Executable;
import com.djrapitops.plan.storage.database.transactions.Transaction;
import org.apache.commons.lang3.StringUtils;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;
import java.util.stream.Collectors;
/**
* Stores the metadata related to an extension graph.
*
* @author AuroraLS3
*/
public class StoreGraphPointProviderTransaction extends Transaction {
private final ExtensionMethod method;
private final ProviderInformation info;
private final ServerUUID serverUUID;
private final GraphPointProvider annotation;
public StoreGraphPointProviderTransaction(
GraphPointProvider annotation,
ExtensionMethod method,
ProviderInformation info,
ServerUUID serverUUID
) {
this.annotation = annotation;
this.method = method;
this.info = info;
this.serverUUID = serverUUID;
}
private static @NotNull List<String> truncate(String[] colors, int colorMaxLength) {
return Arrays.stream(colors).map(s -> StringUtils.truncate(s, colorMaxLength))
.collect(Collectors.toList());
}
@Override
protected void performOperations() {
executeOther(new StoreProviderTransaction(info, serverUUID));
commitMidTransaction();
execute(storeMetadata());
execute(ExtensionGraphMetadataTable.createGraphTableSQL(dbType, info.getPluginName(), method.getMethodName(), ExtensionGraphMetadataTable.TableType.SERVER));
storeAggregateTypes();
storeAggregateTypeLinks();
storeColors();
storeColorLinks();
storeFormats();
storeFormatLinks();
storeUnits();
storeUnitLinks();
}
private void storeColorLinks() {
List<String> colors = truncate(annotation.seriesColors(), ExtensionGraphColorTable.COLOR_MAX_LENGTH);
String selectColumnCount = ExtensionGraphColorTable.ToProviderTable.SELECT_COLUMN_COUNT;
String updateStatement = ExtensionGraphColorTable.ToProviderTable.UPDATE_STATEMENT;
String deleteStatement = ExtensionGraphColorTable.ToProviderTable.DELETE_STATEMENT;
String insertStatement = ExtensionGraphColorTable.ToProviderTable.INSERT_STATEMENT;
storeItemLinks(selectColumnCount, colors, updateStatement, deleteStatement, insertStatement);
}
private void storeFormatLinks() {
List<String> formats = Arrays.stream(annotation.valueFormats()).map(FormatType::name).collect(Collectors.toList());
String selectColumnCount = ExtensionGraphFormatTable.ToProviderTable.SELECT_COLUMN_COUNT;
String updateStatement = ExtensionGraphFormatTable.ToProviderTable.UPDATE_STATEMENT;
String deleteStatement = ExtensionGraphFormatTable.ToProviderTable.DELETE_STATEMENT;
String insertStatement = ExtensionGraphFormatTable.ToProviderTable.INSERT_STATEMENT;
storeItemLinks(selectColumnCount, formats, updateStatement, deleteStatement, insertStatement);
}
private void storeUnitLinks() {
List<String> units = truncate(annotation.unitNames(), ExtensionGraphUnitTable.UNIT_MAX_LENGTH);
String selectColumnCount = ExtensionGraphUnitTable.ToProviderTable.SELECT_COLUMN_COUNT;
String updateStatement = ExtensionGraphUnitTable.ToProviderTable.UPDATE_STATEMENT;
String deleteStatement = ExtensionGraphUnitTable.ToProviderTable.DELETE_STATEMENT;
String insertStatement = ExtensionGraphUnitTable.ToProviderTable.INSERT_STATEMENT;
storeItemLinks(selectColumnCount, units, updateStatement, deleteStatement, insertStatement);
}
private void storeItemLinks(String selectColumnCount, List<String> colors, String updateStatement, String deleteStatement, String insertStatement) {
int storedColumnCount = query(db -> db.queryOptional(selectColumnCount,
set -> set.getInt(1), info.getName(), info.getPluginName(), serverUUID))
.orElse(0);
int columnCount = colors.size();
if (storedColumnCount >= columnCount) {
// More columns stored than what we have
// update 0 -> count, delete count -> storedCount
execute(new ExecBatchStatement(updateStatement) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (int i = 0; i < columnCount; i++) {
statement.setString(1, colors.get(i));
statement.setInt(2, i);
ExtensionProviderTable.set3PluginValuesToStatement(statement, 3, info.getName(), info.getPluginName(), serverUUID);
}
}
});
execute(new ExecBatchStatement(deleteStatement) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (int i = columnCount; i < storedColumnCount; i++) {
ExtensionProviderTable.set3PluginValuesToStatement(statement, 1, info.getName(), info.getPluginName(), serverUUID);
statement.setInt(4, i);
}
}
});
} else {
// Fewer columns stored than what we have.
// update 0 -> storedCount, insert storedCount -> count
execute(new ExecBatchStatement(updateStatement) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (int i = 0; i < storedColumnCount; i++) {
statement.setString(1, colors.get(i));
statement.setInt(2, i);
ExtensionProviderTable.set3PluginValuesToStatement(statement, 3, info.getName(), info.getPluginName(), serverUUID);
}
}
});
execute(new ExecBatchStatement(insertStatement) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (int i = storedColumnCount; i < columnCount; i++) {
statement.setString(1, colors.get(i));
statement.setInt(2, i);
ExtensionProviderTable.set3PluginValuesToStatement(statement, 3, info.getName(), info.getPluginName(), serverUUID);
}
}
});
}
}
private void storeColors() {
String[] colors = annotation.seriesColors();
String insertStatement = ExtensionGraphColorTable.INSERT_STATEMENT;
Optional<String> selectStatement = ExtensionGraphColorTable.selectInSql(colors.length);
storeItems(truncate(colors, ExtensionGraphColorTable.COLOR_MAX_LENGTH), insertStatement, selectStatement);
}
private void storeFormats() {
FormatType[] formats = annotation.valueFormats();
String insertStatement = ExtensionGraphFormatTable.INSERT_STATEMENT;
Optional<String> selectStatement = ExtensionGraphFormatTable.selectInSql(formats.length);
storeItems(Arrays.stream(formats).map(FormatType::name).collect(Collectors.toList()), insertStatement, selectStatement);
}
private void storeUnits() {
String[] units = annotation.unitNames();
String insertStatement = ExtensionGraphUnitTable.INSERT_STATEMENT;
Optional<String> selectStatement = ExtensionGraphUnitTable.selectInSql(units.length);
storeItems(truncate(units, ExtensionGraphUnitTable.UNIT_MAX_LENGTH), insertStatement, selectStatement);
}
private void storeItems(Collection<String> items, String insertStatement,
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional<String> selectStatement) {
Set<String> storedItems = selectStatement.map(sql ->
query(db -> db.querySet(sql, set -> set.getString(1), items))
).orElse(Set.of());
if (storedItems.size() == items.size()) return;
execute(new ExecBatchStatement(insertStatement) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (String color : items) {
if (storedItems.contains(color)) continue;
statement.setString(1, color);
statement.addBatch();
}
}
});
}
private void storeAggregateTypeLinks() {
Aggregates[] aggregates = annotation.supportedAggregateFunctions();
if (aggregates.length > 0) {
// Delete aggregates of this provider
execute(new ExecStatement(ExtensionGraphAggregateTypeTable.ToProviderTable.DELETE_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
ExtensionProviderTable.set3PluginValuesToStatement(statement, 1, info.getName(), info.getPluginName(), serverUUID);
}
});
// Insert new aggregates
execute(new ExecStatement(ExtensionGraphAggregateTypeTable.ToProviderTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (Aggregates aggregate : aggregates) {
statement.setString(1, aggregate.name());
ExtensionProviderTable.set3PluginValuesToStatement(statement, 2, info.getName(), info.getPluginName(), serverUUID);
statement.addBatch();
}
}
});
}
}
private void storeAggregateTypes() {
List<String> existingAggregates = query(db -> db.queryList(ExtensionGraphAggregateTypeTable.selectInSQL(Aggregates.values().length),
row -> row.getString(ExtensionGraphAggregateTypeTable.AGGREGATE_TYPE),
Arrays.stream(Aggregates.values()).map(Aggregates::name).collect(Collectors.toList())));
List<String> newAggregates = Arrays.stream(Aggregates.values())
.map(Aggregates::name)
.filter(existingAggregates::contains)
.collect(Collectors.toList());
if (!newAggregates.isEmpty()) {
execute(storeAggregateTypes(newAggregates));
}
}
private Executable storeAggregateTypes(List<String> newAggregates) {
return new ExecBatchStatement(ExtensionGraphAggregateTypeTable.INSERT_STATEMENT) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
for (String newAggregate : newAggregates) {
statement.setString(1, newAggregate);
statement.addBatch();
}
}
};
}
private Executable storeMetadata() {
return connection -> {
if (!updateMetadata().execute(connection)) {
return insertMetadata().execute(connection);
}
return false;
};
}
private Executable insertMetadata() {
return executeStatement(ExtensionGraphMetadataTable.INSERT_STATEMENT);
}
private @NotNull ExecStatement executeStatement(@Language("SQL") String sql) {
return new ExecStatement(sql) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setBoolean(1, ExtensionExtractor.actuallySupportsStacking(annotation));
statement.setInt(2, annotation.yAxisSoftMax());
statement.setInt(3, annotation.yAxisSoftMin());
statement.setInt(4, annotation.xAxisSoftMax());
statement.setInt(5, annotation.xAxisSoftMin());
statement.setString(6, ExtensionGraphMetadataTable.getTableName(info.getPluginName(), method.getMethodName()));
statement.setInt(7, getTableType().getType());
ExtensionTabTable.set3TabValuesToStatement(statement, 8, info.getTab().orElse(null), info.getPluginName(), serverUUID);
ExtensionProviderTable.set3PluginValuesToStatement(statement, 11, info.getName(), info.getPluginName(), serverUUID);
}
};
}
private ExtensionGraphMetadataTable.TableType getTableType() {
switch (method.getParameterType()) {
case SERVER_NONE:
return ExtensionGraphMetadataTable.TableType.SERVER;
case PLAYER_STRING:
case PLAYER_UUID:
return ExtensionGraphMetadataTable.TableType.PLAYER;
case GROUP:
return ExtensionGraphMetadataTable.TableType.GROUP;
default:
throw new DBOpException("Unsupported method type " + method.getParameterType());
}
}
private Executable updateMetadata() {
return executeStatement(ExtensionGraphMetadataTable.UPDATE_STATEMENT);
}
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionGraphQueries;
import com.djrapitops.plan.storage.database.transactions.Transaction;
import java.util.List;
/**
* Drops all existing extension graph tables.
*
* @author AuroraLS3
*/
public class RemoveGraphTablesTransaction extends Transaction {
@Override
protected void performOperations() {
List<String> tableNames = query(ExtensionGraphQueries.findGraphTableNames());
tableNames.forEach(this::dropTable);
}
private void dropTable(String tableName) {
execute("DROP TABLE IF EXISTS " + tableName);
}
}

View File

@ -0,0 +1,69 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.extension.graph.DataPoint;
import com.djrapitops.plan.extension.implementation.providers.ProviderIdentifier;
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.ExpandGraphColumnCountTransaction;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.ExtensionGraphMetadataTable;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.Transaction;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.UUID;
/**
* @author AuroraLS3
*/
public class StorePlayerGraphPoint extends Transaction {
private final DataPoint dataPoint;
private final UUID playerUUID;
private final ProviderIdentifier identifier;
public StorePlayerGraphPoint(DataPoint dataPoint, UUID playerUUID, ProviderIdentifier identifier) {
this.dataPoint = dataPoint;
this.playerUUID = playerUUID;
this.identifier = identifier;
}
@Override
protected void performOperations() {
int columnCount = dataPoint.getValues().size();
executeOther(new ExpandGraphColumnCountTransaction(identifier, columnCount));
execute(new ExecStatement(ExtensionGraphMetadataTable.insertToGraphTableSql(identifier.getPluginName(), identifier.getProviderName(), columnCount,
ExtensionGraphMetadataTable.TableType.PLAYER)) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, getServerUUID().toString());
statement.setLong(2, dataPoint.getX());
for (int i = 0; i < columnCount; i++) {
Double value = dataPoint.getValues().get(i);
if (value == null) {
statement.setNull(i + 3, Types.DOUBLE);
} else {
statement.setDouble(i + 3, value);
}
}
statement.setString(columnCount + 1, playerUUID.toString());
}
});
}
}

View File

@ -0,0 +1,67 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.extension.implementation.storage.transactions.results;
import com.djrapitops.plan.extension.graph.DataPoint;
import com.djrapitops.plan.extension.implementation.providers.ProviderIdentifier;
import com.djrapitops.plan.extension.implementation.storage.transactions.providers.ExpandGraphColumnCountTransaction;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.ExtensionGraphMetadataTable;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.Transaction;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
/**
* Stores a server specific datapoint to extension graph table.
*
* @author AuroraLS3
*/
public class StoreServerGraphPoint extends Transaction {
private final DataPoint dataPoint;
private final ProviderIdentifier identifier;
public StoreServerGraphPoint(DataPoint dataPoint, ProviderIdentifier identifier) {
this.dataPoint = dataPoint;
this.identifier = identifier;
}
@Override
protected void performOperations() {
int columnCount = dataPoint.getValues().size();
executeOther(new ExpandGraphColumnCountTransaction(identifier, columnCount));
execute(new ExecStatement(ExtensionGraphMetadataTable.insertToGraphTableSql(identifier.getPluginName(), identifier.getProviderName(), columnCount,
ExtensionGraphMetadataTable.TableType.SERVER)) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, getServerUUID().toString());
statement.setLong(2, dataPoint.getX());
for (int i = 0; i < dataPoint.getValues().size(); i++) {
Double value = dataPoint.getValues().get(i);
if (value == null) {
statement.setNull(i + 3, Types.DOUBLE);
} else {
statement.setDouble(i + 3, value);
}
}
}
});
}
}

View File

@ -35,7 +35,6 @@ public class PlayerSwitchServerEventConsumer {
this.serverInfo = serverInfo;
}
// TODO introduce an abstract event/interface for leave/join/switch since they share code.
public void onServerSwitch(PlatformPlayerData player, long time) {
PlayerJoin asJoin = PlayerJoin.builder()
.player(player)

View File

@ -0,0 +1,86 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.sql.tables.extension.graph;
import com.djrapitops.plan.storage.database.DBType;
import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionProviderTable;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Represents extension_graph_aggregate_type table.
*
* @author AuroraLS3
*/
public class ExtensionGraphAggregateTypeTable {
public static final String TABLE_NAME = "extension_graph_aggregate_type";
public static final String ID = "id";
public static final String AGGREGATE_TYPE = "aggregate_type";
public static final String SELECT_ID_STATEMENT = SELECT + ID + FROM + TABLE_NAME + WHERE + AGGREGATE_TYPE + "=?";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + AGGREGATE_TYPE + ") VALUES (?)";
private ExtensionGraphAggregateTypeTable() {
/* Static sql utility class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(AGGREGATE_TYPE, Sql.varchar(25)).notNull()
.build();
}
public static String selectInSQL(int n) {
// Nothing to select when n == 0
return SELECT + '*' + FROM + TABLE_NAME + WHERE + AGGREGATE_TYPE + (n > 0 ? " IN (" + Sql.nParameters(n) + ")" : IS_NULL);
}
/**
* Represents extension_graph_aggregate_type_to_graph table that joins extension_graph_aggregate_type to a specific graph.
*/
public static class ToProviderTable {
public static final String TABLE_NAME = "extension_graph_aggregate_type_to_graph";
public static final String ID = "id";
public static final String AGGREGATE_TYPE_ID = "aggregate_type_id";
public static final String PROVIDER_ID = "provider_id";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + AGGREGATE_TYPE_ID + ',' + PROVIDER_ID +
") VALUES (" + SELECT_ID_STATEMENT + ", " + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID + ")";
public static final String DELETE_STATEMENT = "DELETE" + FROM + TABLE_NAME +
WHERE + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
private ToProviderTable() {
/* Static sql utility class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(AGGREGATE_TYPE_ID, Sql.INT)
.column(PROVIDER_ID, Sql.INT)
.foreignKey(AGGREGATE_TYPE_ID, ExtensionGraphAggregateTypeTable.TABLE_NAME, ExtensionGraphAggregateTypeTable.ID)
.foreignKey(PROVIDER_ID, ExtensionProviderTable.TABLE_NAME, ExtensionProviderTable.ID)
.build();
}
}
}

View File

@ -0,0 +1,100 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.sql.tables.extension.graph;
import com.djrapitops.plan.storage.database.DBType;
import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionProviderTable;
import java.util.Optional;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Represents extension_graph_color table.
*
* @author AuroraLS3
*/
public class ExtensionGraphColorTable {
public static final String TABLE_NAME = "extension_graph_color";
public static final String ID = "id";
public static final String COLOR = "color";
public static final String SELECT_ID_STATEMENT = SELECT + ID + FROM + TABLE_NAME + WHERE + COLOR + "=?";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + COLOR + ") VALUES (?)";
public static final int COLOR_MAX_LENGTH = 7;
private ExtensionGraphColorTable() {
/* Static sql utility class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(COLOR, Sql.varchar(COLOR_MAX_LENGTH))
.build();
}
public static Optional<String> selectInSql(int n) {
if (n == 0) {
return Optional.empty();
}
return Optional.of(SELECT + COLOR + FROM + TABLE_NAME + WHERE + COLOR + " IN (" + Sql.nParameters(n) + ")");
}
/**
* Represents extension_graph_color_to_graph table that joins extension_graph_color to a specific graph and column.
*/
public static class ToProviderTable {
public static final String TABLE_NAME = "extension_graph_color_to_graph";
public static final String ID = "id";
public static final String COLOR_ID = "color_id";
public static final String PROVIDER_ID = "provider_id";
public static final String COLUMN_INDEX = "column_index";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + COLOR_ID + ',' + COLUMN_INDEX + ',' + PROVIDER_ID +
") VALUES (" + SELECT_ID_STATEMENT + ",?," + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID + ")";
public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET " + COLOR_ID + "=" + SELECT_ID_STATEMENT +
WHERE + COLUMN_INDEX + "=?" +
AND + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
public static final String DELETE_STATEMENT = "DELETE" + FROM + TABLE_NAME +
WHERE + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID +
AND + COLUMN_INDEX + "=?";
public static final String SELECT_COLUMN_COUNT = SELECT + "COUNT(*)" +
FROM + TABLE_NAME +
WHERE + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
private ToProviderTable() {
/* Static sql utility class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(COLOR_ID, Sql.INT)
.column(PROVIDER_ID, Sql.INT)
.column(COLUMN_INDEX, Sql.INT).notNull()
.foreignKey(COLOR_ID, ExtensionGraphColorTable.TABLE_NAME, ExtensionGraphColorTable.ID)
.foreignKey(PROVIDER_ID, ExtensionProviderTable.TABLE_NAME, ExtensionProviderTable.ID)
.build();
}
}
}

View File

@ -0,0 +1,99 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.sql.tables.extension.graph;
import com.djrapitops.plan.storage.database.DBType;
import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionProviderTable;
import java.util.Optional;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Represents extension_graph_format table.
*
* @author AuroraLS3
*/
public class ExtensionGraphFormatTable {
public static final String TABLE_NAME = "extension_graph_format";
public static final String ID = "id";
public static final String FORMAT = "format";
public static final String SELECT_ID_STATEMENT = SELECT + ID + FROM + TABLE_NAME + WHERE + FORMAT + "=?";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + FORMAT + ") VALUES (?)";
private ExtensionGraphFormatTable() {
/* Static sql utility class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(FORMAT, Sql.varchar(15))
.build();
}
public static Optional<String> selectInSql(int n) {
if (n == 0) {
return Optional.empty();
}
return Optional.of(SELECT + FORMAT + FROM + TABLE_NAME + WHERE + FORMAT + " IN (" + Sql.nParameters(n) + ")");
}
/**
* Represents extension_graph_format_to_graph table that joins extension_graph_format to a specific graph and column.
*/
public static class ToProviderTable {
public static final String TABLE_NAME = "extension_graph_format_to_graph";
public static final String ID = "id";
public static final String FORMAT_ID = "format_id";
public static final String PROVIDER_ID = "provider_id";
public static final String COLUMN_INDEX = "column_index";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + FORMAT_ID + ',' + COLUMN_INDEX + ',' + PROVIDER_ID +
") VALUES (" + SELECT_ID_STATEMENT + ",?," + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID + ")";
public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET " + FORMAT_ID + "=" + SELECT_ID_STATEMENT +
WHERE + COLUMN_INDEX + "=?" +
AND + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
public static final String DELETE_STATEMENT = "DELETE" + FROM + TABLE_NAME +
WHERE + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID +
AND + COLUMN_INDEX + "=?";
public static final String SELECT_COLUMN_COUNT = SELECT + "COUNT(*)" +
FROM + TABLE_NAME +
WHERE + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
private ToProviderTable() {
/* Static sql utility class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(FORMAT_ID, Sql.INT)
.column(PROVIDER_ID, Sql.INT)
.column(COLUMN_INDEX, Sql.INT).notNull()
.foreignKey(FORMAT_ID, ExtensionGraphFormatTable.TABLE_NAME, ExtensionGraphFormatTable.ID)
.foreignKey(PROVIDER_ID, ExtensionProviderTable.TABLE_NAME, ExtensionProviderTable.ID)
.build();
}
}
}

View File

@ -0,0 +1,181 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.sql.tables.extension.graph;
import com.djrapitops.plan.exceptions.database.DBOpException;
import com.djrapitops.plan.storage.database.DBType;
import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.ServerTable;
import com.djrapitops.plan.storage.database.sql.tables.UsersTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionProviderTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionTabTable;
import org.apache.commons.text.TextStringBuilder;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Represents plan_extension_graph_metadata table.
*
* @author AuroraLS3
*/
public class ExtensionGraphMetadataTable {
public static final String TABLE_NAME = "plan_extension_graph_metadata";
public static final String ID = "id";
public static final String PROVIDER_ID = "provider_id";
public static final String TAB_ID = "tab_id";
public static final String SUPPORTS_STACKING = "supports_stacking";
public static final String Y_AXIS_SOFT_MAX = "y_axis_soft_max";
public static final String Y_AXIS_SOFT_MIN = "y_axis_soft_min";
public static final String X_AXIS_SOFT_MAX = "x_axis_soft_max";
public static final String X_AXIS_SOFT_MIN = "x_axis_soft_min";
public static final String GRAPH_TABLE_NAME = "graph_table_name";
public static final String COLUMN_COUNT = "column_count";
public static final String TABLE_TYPE = "table_type";
@Language("SQL")
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" +
SUPPORTS_STACKING + ',' +
Y_AXIS_SOFT_MAX + ',' +
Y_AXIS_SOFT_MIN + ',' +
X_AXIS_SOFT_MAX + ',' +
X_AXIS_SOFT_MIN + ',' +
GRAPH_TABLE_NAME + ',' +
TABLE_TYPE + ',' +
TAB_ID + ',' +
PROVIDER_ID +
") VALUES (?, ?, ?, ?, ?, ?, ?," +
ExtensionTabTable.STATEMENT_SELECT_TAB_ID + ',' +
ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID +
")";
@Language("SQL")
public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET " +
SUPPORTS_STACKING + "=?," +
Y_AXIS_SOFT_MAX + "=?," +
Y_AXIS_SOFT_MIN + "=?," +
X_AXIS_SOFT_MAX + "=?," +
X_AXIS_SOFT_MIN + "=?," +
GRAPH_TABLE_NAME + "=?," +
TABLE_TYPE + "=?," +
TAB_ID + "=" + ExtensionTabTable.STATEMENT_SELECT_TAB_ID +
WHERE +
PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
@Language("SQL")
public static final String STATEMENT_SELECT_COLUMN_COUNT = SELECT + COLUMN_COUNT +
FROM + TABLE_NAME +
WHERE + PROVIDER_ID + '=' + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
@Language("SQL")
public static final String UPDATE_COLUMN_COUNT_STATEMENT = "UPDATE " + TABLE_NAME + " SET " +
COLUMN_COUNT + "=?" +
WHERE +
PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
private ExtensionGraphMetadataTable() {
/* Static information class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(PROVIDER_ID, Sql.INT).notNull()
.column(TAB_ID, Sql.INT)
.column(SUPPORTS_STACKING, Sql.BOOL).notNull()
.column(Y_AXIS_SOFT_MAX, Sql.LONG).notNull()
.column(Y_AXIS_SOFT_MIN, Sql.LONG).notNull()
.column(X_AXIS_SOFT_MAX, Sql.LONG).notNull()
.column(X_AXIS_SOFT_MIN, Sql.LONG).notNull()
.column(GRAPH_TABLE_NAME, Sql.varchar(116)).notNull() // plan_extension_pluginName_methodName
.column(COLUMN_COUNT, Sql.INT).defaultValue("1").notNull()
.column(TABLE_TYPE, INT).notNull()
.foreignKey(PROVIDER_ID, ExtensionProviderTable.TABLE_NAME, ExtensionProviderTable.ID)
.foreignKey(TAB_ID, ExtensionTabTable.TABLE_NAME, ExtensionTabTable.ID)
.build();
}
public static String createGraphTableSQL(DBType dbType, String pluginName, String methodName, TableType type) {
String serverIdColumn = "server_id";
if (type == TableType.SERVER) {
return CreateTableBuilder.create(getTableName(pluginName, methodName), dbType)
.column(ID, Sql.INT).primaryKey()
.column(serverIdColumn, INT).notNull()
.column("x", Sql.LONG).notNull()
.column("value_1", Sql.DOUBLE)
.foreignKey(serverIdColumn, ServerTable.TABLE_NAME, ServerTable.ID)
.build();
} else if (type == TableType.PLAYER) {
return CreateTableBuilder.create(getTableName(pluginName, methodName), dbType)
.column(ID, Sql.INT).primaryKey()
.column(serverIdColumn, INT).notNull()
.column("x", Sql.LONG).notNull()
.column("value_1", Sql.DOUBLE)
.column("user_id", INT)
.foreignKey("user_id", UsersTable.TABLE_NAME, UsersTable.ID)
.foreignKey(serverIdColumn, ServerTable.TABLE_NAME, ServerTable.ID)
.build();
}
throw new DBOpException("Unsupported table type " + type.name());
}
public static @NotNull String getTableName(String pluginName, String methodName) {
return "plan_extension_" + pluginName + "_" + methodName;
}
public static List<String> addColumnsStatements(String pluginName, String methodName, int columnCount, int newColumnCount) {
return IntStream.range(columnCount, newColumnCount)
.mapToObj(i -> "ALTER TABLE " + getTableName(pluginName, methodName) + " ADD COLUMN value_" + (i + 1) + " " + DOUBLE)
.collect(Collectors.toList());
}
public static String insertToGraphTableSql(String pluginName, String methodName, int columnCount, TableType type) {
String valueList = new TextStringBuilder().appendWithSeparators(IntStream.range(0, columnCount)
.mapToObj(i -> "value_" + (i + 1))
.iterator(), ",")
.toString();
return "INSERT INTO " + getTableName(pluginName, methodName) + " (" +
"server_id," +
valueList +
(type == TableType.PLAYER ? ",user_id" : "") +
") VALUES (" +
ServerTable.SELECT_SERVER_ID + ',' +
Sql.nParameters(columnCount) +
(type == TableType.PLAYER ? ',' + UsersTable.SELECT_USER_ID : "") +
")";
}
public enum TableType {
SERVER(0),
PLAYER(1),
GROUP(2);
private final int type;
TableType(int type) {
this.type = type;
}
public int getType() {
return type;
}
}
}

View File

@ -0,0 +1,100 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.sql.tables.extension.graph;
import com.djrapitops.plan.storage.database.DBType;
import com.djrapitops.plan.storage.database.sql.building.CreateTableBuilder;
import com.djrapitops.plan.storage.database.sql.building.Sql;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionProviderTable;
import java.util.Optional;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
/**
* Represents extension_graph_unit table.
*
* @author AuroraLS3
*/
public class ExtensionGraphUnitTable {
public static final String TABLE_NAME = "extension_graph_unit";
public static final String ID = "id";
public static final String UNIT = "unit";
public static final int UNIT_MAX_LENGTH = 50;
public static final String SELECT_ID_STATEMENT = SELECT + ID + FROM + TABLE_NAME + WHERE + UNIT + "=?";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + UNIT + ") VALUES (?)";
private ExtensionGraphUnitTable() {
/* Static sql utility class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(UNIT, Sql.varchar(UNIT_MAX_LENGTH))
.build();
}
public static Optional<String> selectInSql(int n) {
if (n == 0) {
return Optional.empty();
}
return Optional.of(SELECT + UNIT + FROM + TABLE_NAME + WHERE + UNIT + " IN (" + Sql.nParameters(n) + ")");
}
/**
* Represents extension_graph_unit_to_graph table that joins extension_graph_unit to a specific graph and column.
*/
public static class ToProviderTable {
public static final String TABLE_NAME = "extension_graph_unit_to_graph";
public static final String ID = "id";
public static final String UNIT_ID = "unit_id";
public static final String PROVIDER_ID = "provider_id";
public static final String COLUMN_INDEX = "column_index";
public static final String INSERT_STATEMENT = "INSERT INTO " + TABLE_NAME + " (" + UNIT_ID + ',' + COLUMN_INDEX + ',' + PROVIDER_ID +
") VALUES (" + SELECT_ID_STATEMENT + ",?," + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID + ")";
public static final String UPDATE_STATEMENT = "UPDATE " + TABLE_NAME + " SET " + UNIT_ID + "=" + SELECT_ID_STATEMENT +
WHERE + COLUMN_INDEX + "=?" +
AND + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
public static final String DELETE_STATEMENT = "DELETE" + FROM + TABLE_NAME +
WHERE + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID +
AND + COLUMN_INDEX + "=?";
public static final String SELECT_COLUMN_COUNT = SELECT + "COUNT(*)" +
FROM + TABLE_NAME +
WHERE + PROVIDER_ID + "=" + ExtensionProviderTable.STATEMENT_SELECT_PROVIDER_ID;
private ToProviderTable() {
/* Static sql utility class */
}
public static String createTableSQL(DBType dbType) {
return CreateTableBuilder.create(TABLE_NAME, dbType)
.column(ID, Sql.INT).primaryKey()
.column(UNIT_ID, Sql.INT)
.column(PROVIDER_ID, Sql.INT)
.column(COLUMN_INDEX, Sql.INT).notNull()
.foreignKey(UNIT_ID, ExtensionGraphUnitTable.TABLE_NAME, ExtensionGraphUnitTable.ID)
.foreignKey(PROVIDER_ID, ExtensionProviderTable.TABLE_NAME, ExtensionProviderTable.ID)
.build();
}
}
}

View File

@ -16,8 +16,10 @@
*/
package com.djrapitops.plan.storage.database.transactions.commands;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionGraphQueries;
import com.djrapitops.plan.storage.database.queries.objects.BaseUserQueries;
import com.djrapitops.plan.storage.database.sql.tables.*;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.ExtensionGraphMetadataTable;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.Executable;
@ -52,6 +54,8 @@ public class CombineUserTransaction extends ChangeUserUUIDTransaction {
execute(updateUserId(PingTable.TABLE_NAME, PingTable.USER_ID, oldId, newId));
execute(updateUserId(SessionsTable.TABLE_NAME, SessionsTable.USER_ID, oldId, newId));
execute(updateUserId(WorldTimesTable.TABLE_NAME, WorldTimesTable.USER_ID, oldId, newId));
query(ExtensionGraphQueries.findGraphTableNames(ExtensionGraphMetadataTable.TableType.PLAYER))
.forEach(tableName -> execute(updateUserId(tableName, "user_id", oldId, newId)));
execute(updateUserInfo(newId, oldId));
execute(DELETE_FROM + UserInfoTable.TABLE_NAME + WHERE + UserInfoTable.USER_ID + "=" + oldId);

View File

@ -16,8 +16,10 @@
*/
package com.djrapitops.plan.storage.database.transactions.commands;
import com.djrapitops.plan.extension.implementation.storage.transactions.results.RemoveGraphTablesTransaction;
import com.djrapitops.plan.storage.database.sql.tables.*;
import com.djrapitops.plan.storage.database.sql.tables.extension.*;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.*;
import com.djrapitops.plan.storage.database.sql.tables.webuser.*;
import com.djrapitops.plan.storage.database.transactions.events.StoreJoinAddressTransaction;
import com.djrapitops.plan.storage.database.transactions.patches.Patch;
@ -67,6 +69,17 @@ public class RemoveEverythingTransaction extends Patch {
clearTable(ExtensionPluginTable.TABLE_NAME);
clearTable(ExtensionIconTable.TABLE_NAME);
executeOther(new RemoveGraphTablesTransaction());
clearTable(ExtensionGraphUnitTable.ToProviderTable.TABLE_NAME);
clearTable(ExtensionGraphFormatTable.ToProviderTable.TABLE_NAME);
clearTable(ExtensionGraphColorTable.ToProviderTable.TABLE_NAME);
clearTable(ExtensionGraphAggregateTypeTable.ToProviderTable.TABLE_NAME);
clearTable(ExtensionGraphUnitTable.TABLE_NAME);
clearTable(ExtensionGraphFormatTable.TABLE_NAME);
clearTable(ExtensionGraphColorTable.TABLE_NAME);
clearTable(ExtensionGraphAggregateTypeTable.TABLE_NAME);
clearTable(ExtensionGraphMetadataTable.TABLE_NAME);
executeOther(new StoreJoinAddressTransaction(JoinAddressTable.DEFAULT_VALUE_FOR_LOOKUP));
}

View File

@ -16,11 +16,13 @@
*/
package com.djrapitops.plan.storage.database.transactions.commands;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionGraphQueries;
import com.djrapitops.plan.storage.database.queries.PlayerFetchQueries;
import com.djrapitops.plan.storage.database.sql.tables.*;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionGroupsTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionPlayerTableValueTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.ExtensionPlayerValueTable;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.ExtensionGraphMetadataTable;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.ThrowawayTransaction;
@ -59,6 +61,8 @@ public class RemovePlayerTransaction extends ThrowawayTransaction {
deleteFromUserIdTable(SessionsTable.TABLE_NAME);
deleteFromUserIdTable(PingTable.TABLE_NAME);
deleteFromUserIdTable(UserInfoTable.TABLE_NAME);
query(ExtensionGraphQueries.findGraphTableNames(ExtensionGraphMetadataTable.TableType.PLAYER))
.forEach(this::deleteFromUserIdTable);
deleteFromTable(UsersTable.TABLE_NAME);
deleteFromTable(ExtensionPlayerTableValueTable.TABLE_NAME);

View File

@ -18,6 +18,7 @@ package com.djrapitops.plan.storage.database.transactions.init;
import com.djrapitops.plan.storage.database.sql.tables.*;
import com.djrapitops.plan.storage.database.sql.tables.extension.*;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.*;
import com.djrapitops.plan.storage.database.sql.tables.webuser.*;
import com.djrapitops.plan.storage.database.transactions.events.StoreJoinAddressTransaction;
import com.djrapitops.plan.storage.database.transactions.patches.SecurityTableIdPatch;
@ -72,5 +73,10 @@ public class CreateTablesTransaction extends OperationCriticalTransaction {
execute(ExtensionPlayerTableValueTable.createTableSQL(dbType));
execute(ExtensionServerTableValueTable.createTableSQL(dbType));
execute(ExtensionGroupsTable.createTableSQL(dbType));
execute(ExtensionGraphMetadataTable.createTableSQL(dbType));
execute(ExtensionGraphUnitTable.createTableSQL(dbType));
execute(ExtensionGraphFormatTable.createTableSQL(dbType));
execute(ExtensionGraphColorTable.createTableSQL(dbType));
execute(ExtensionGraphAggregateTypeTable.createTableSQL(dbType));
}
}

View File

@ -16,11 +16,13 @@
*/
package com.djrapitops.plan.storage.database.transactions.init;
import com.djrapitops.plan.extension.implementation.storage.queries.ExtensionGraphQueries;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.settings.config.ExtensionSettings;
import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.sql.tables.extension.*;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.*;
import com.djrapitops.plan.storage.database.transactions.ExecStatement;
import com.djrapitops.plan.storage.database.transactions.ThrowawayTransaction;
@ -51,7 +53,10 @@ public class RemoveOldExtensionsTransaction extends ThrowawayTransaction {
@Override
protected void performOperations() {
for (Integer providerID : query(inactiveProviderIDsQuery())) {
Collection<Integer> inactiveProviderIds = query(inactiveProviderIDsQuery());
query(ExtensionGraphQueries.findGraphTableNames(inactiveProviderIds))
.forEach(tableName -> execute("DROP TABLE IF EXISTS " + tableName));
for (Integer providerID : inactiveProviderIds) {
removeValues(providerID);
}
for (Integer providerID : query(inactiveTableProviderIDsQuery())) {
@ -64,14 +69,14 @@ public class RemoveOldExtensionsTransaction extends ThrowawayTransaction {
for (String table : new String[]{
ExtensionPlayerValueTable.TABLE_NAME,
ExtensionServerValueTable.TABLE_NAME,
ExtensionGroupsTable.TABLE_NAME
ExtensionGroupsTable.TABLE_NAME,
ExtensionGraphUnitTable.ToProviderTable.TABLE_NAME,
ExtensionGraphFormatTable.ToProviderTable.TABLE_NAME,
ExtensionGraphColorTable.ToProviderTable.TABLE_NAME,
ExtensionGraphAggregateTypeTable.ToProviderTable.TABLE_NAME,
ExtensionGraphMetadataTable.TABLE_NAME
}) {
execute(new ExecStatement(DELETE_FROM + table + WHERE + "provider_id=?") {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, providerID);
}
});
execute(DELETE_FROM + table + WHERE + "provider_id=" + providerID);
}
}
@ -80,12 +85,7 @@ public class RemoveOldExtensionsTransaction extends ThrowawayTransaction {
ExtensionPlayerTableValueTable.TABLE_NAME,
ExtensionServerTableValueTable.TABLE_NAME
}) {
execute(new ExecStatement(DELETE_FROM + table + WHERE + "table_id=?") {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setInt(1, providerID);
}
});
execute(DELETE_FROM + table + WHERE + "table_id=" + providerID);
}
}

View File

@ -0,0 +1,59 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.sql.tables.extension;
import com.djrapitops.plan.storage.database.sql.tables.extension.graph.ExtensionGraphMetadataTable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import java.util.List;
import static com.djrapitops.plan.storage.database.sql.building.Sql.DOUBLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author AuroraLS3
*/
class ExtensionGraphMetadataTableTest {
@ParameterizedTest(name = "Add columns from {0} to {1} expecting {2} new column alteration statements")
@CsvSource({
"1,1,0",
"1,2,1",
"1,5,4",
"2,3,1",
"2,4,2"
})
void alterTableGenerationSanityTest(int existingColumnCount, int newColumnCount, int expectedAddition) {
List<String> result = ExtensionGraphMetadataTable.addColumnsStatements("test", "test", existingColumnCount, newColumnCount);
assertEquals(expectedAddition, result.size(), () -> "Expanding from " + existingColumnCount + " to " + newColumnCount + " wanted " + expectedAddition + " statements but got " + result.size());
}
@ParameterizedTest(name = "Add column from {0} to {1} should add value_{2} column")
@CsvSource({
"1,2,2",
"2,3,3",
"3,4,4",
"6,7,7",
})
void alterTableGenerationValueNSanityTest(int existingColumnCount, int newColumnCount, int expectedAddition) {
List<String> result = ExtensionGraphMetadataTable.addColumnsStatements("test", "test", existingColumnCount, newColumnCount);
List<String> expected = List.of("ALTER TABLE plan_extension_test_test ADD COLUMN value_" + expectedAddition + " " + DOUBLE);
assertEquals(expected, result);
}
}